溢出和补码
今天在学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
相信大家都知道这是溢出了,int
是 32
位的,能表示带符号位的 -2_147_483_648 ~ 2_147_483_647
,因为 20_000_000_000
超出了这个范围,所以产生了错误。
那么为什么最终的输出结果是 -1_474_836_480
,我决定亲手算一算。当然这是基于补码的,关于补码不清楚的,可以看一下我上一篇博文。
下面是计算过程:
-
对于32位bit来说,它能表示的数是
0
到4_294_967_295
,考虑上符号位后,表示的就是-2_147_483_648 ~ 2_147_483_647
,mod =4_294_967_296
-
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
-
2_820_130_816 > 2_147_483_647
,所以我们知道这是一个负数的补码。在补码中,2_820_130_816表示的是该数关于模的同余数 -
求补数:
4_294_967_296 - 2_820_130_816 = 1_474_836_480
,所以在补码中,这个溢出导致的最终输出是-1_474_836_480
-
回到同余:
-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_480
和2_820_130_816
关于模4_294_967_296
同余
不得不说,同余还真是好用啊!理解了它,就理解了补码