溢出和补码
今天在学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_8162_820_130_816 mod 4_294_967_296 = 2_820_130_816-1_474_836_480和2_820_130_816关于模4_294_967_296同余
不得不说,同余还真是好用啊!理解了它,就理解了补码

浙公网安备 33010602011771号