溢出和补码

今天在学java的时候,看到这样一段代码:

public class Overflow {
    // JDK新特性,数字之间可以用下划线分割
    public static void main(String[] args) {
        int money = 1_000_000_000;   // int是32位的,一共有4_294_967_296种状态,补码世界中表示的范围是 -2_147_483_648 到 2_147_483_647
        int years = 20;
        int total = money * years;  // want = 20_000_000_000 
        System.out.println(total);  // output = -1474836480
    }
}
want = 20_000_000_000
got = -1_474_836_480

相信大家都知道这是溢出了,int32 位的,能表示带符号位的 -2_147_483_648 ~ 2_147_483_647,因为 20_000_000_000超出了这个范围,所以产生了错误。

那么为什么最终的输出结果是 -1_474_836_480,我决定亲手算一算。当然这是基于补码的,关于补码不清楚的,可以看一下我上一篇博文。

下面是计算过程:

  1. 对于32位bit来说,它能表示的数是 04_294_967_295,考虑上符号位后,表示的就是 -2_147_483_648 ~ 2_147_483_647,mod = 4_294_967_296

  2. 1_000_000_000 * 20 = 20_000_000_000 > 2_147_483_647,溢出取余--> 20_000_000_000 mod 4_294_967_296 = 2_820_130_816

  3. 2_820_130_816 > 2_147_483_647,所以我们知道这是一个负数的补码。在补码中,2_820_130_816表示的是该数关于模的同余数

  4. 求补数:4_294_967_296 - 2_820_130_816 = 1_474_836_480,所以在补码中,这个溢出导致的最终输出是 -1_474_836_480

  5. 回到同余:

    -1_474_836_480 mod 4_294_967_296 = 2_820_130_816

    2_820_130_816 mod 4_294_967_296 = 2_820_130_816

    -1_474_836_4802_820_130_816 关于模 4_294_967_296 同余

不得不说,同余还真是好用啊!理解了它,就理解了补码

posted @ 2020-08-18 14:45  宗吾先生  阅读(450)  评论(0编辑  收藏  举报