为什么这段代码会打印出两个负数?

15
int a = Integer.MIN_VALUE;
int b = -a;
System.out.println("a = "+a + " | b = "+b);

结果:

a = -2147483648 | b = -2147483648

我原本期望b是一个正数。


3
因为最大的正数比其实际值小了1,所以在计算中要考虑0,并且正数与负数的数量相等。 - Stultuske
4
整数的最大值为2147483647,因此它会循环回到最小值。如果在将值分配给b之前将1添加到a,则应该可以正常工作。 - AntonH
8
这是二进制补码的一个特点。考虑 byte 类型,取值范围为-128到127... 如果您有一个值为-128的字节 x,那么 -x 的值将是多少? - Jon Skeet
4
每个程序员都必须了解二进制补码算术。同时,数字类型的格式和范围在文档中有所覆盖。 - Lew Bloch
2
如果您期望b是一个正数,那么您期望是多少正数,并且为什么期望如此,鉴于Integer.MAX_VALUE是2147483647? - Andreas
显示剩余2条评论
4个回答

20

改变 Integer.MIN_VALUE 的符号会产生溢出;你看到的就是其结果。


16

如果您曾经做过类似这样的事情:

int a = Integer.MAX_VALUE;
a++;
System.out.println(a);

当某个值超过数据类型的最大值时,它会循环回到最小值。当它小于最小值时,它会循环回到最大值。

在这里也是同样的情况。

对整数的最小值取反,在数学上会得到2147483648,但是由于那个值比最大值还要大1,所以它又会回到最小值,也就是-2147483648。

有趣的事实:取反基本上是将每个位从0变成1,从1变成0,然后再加一。试试对这个值在二进制下长这样:10000000000000000000000000000000 进行取反操作。


5
对于二进制的解释,这是因为有符号整数使用补码存储。

所以,在二进制中最小的负数是1000 0000。要反转符号,需要翻转所有比特位,并加1,如下:

1000 0000 => 0111 1111 + 1 = 1000 0000

这样就回到了起点!

就像上面的例子一样,字节(8位)的有符号范围是-128到127,因此-(-128)为128,即127的倒数,因此它会溢出并返回-128。

至于为什么要使用补码?1 - 1变成了1 +(-1),或者0001 + 1111 = 0000。因此,通过在补码中存储负数,只需取第二个数的补码并将它们相加就可以完成减法(这比向算术逻辑单元(ALU)添加减法电路更有效;因为它已经具有按位非和加法来进行补码操作)。


1
通常情况下,当我们尝试为 int 类型分配一个超过其最大值的值时,它会从最小值重新开始循环,例如:
2147483648 -will become-> -2147483648
2147483649 -will become-> -2147483647
2147483650 -will become-> -2147483646

在第一条语句之后,a的值为-2147483648
当执行int b = -a;时,通常b应该具有。
b = -a --> -(-2147483648) --> 2147483648

但 int 的最大正值是 2147483647,而 21474836482147483647 大 1,它将循环回来并变成 -2147483648

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接