Java中的溢出短整型

3
我有一个关于Java中short数据类型的问题。我知道short的范围在-32768到32767之间。
因此,如果我尝试添加两个超出范围的short值,则结果将是所谓的总和减去正范围或负范围乘以2,如下所示:
short a = 30000;
a = (short) (a+a);

结果为-5536

所以这个计算是32768 + 32768 = 655366000 - 65536 = -5536

我知道它的功能,但不知道为什么要这样做。

有人能解释一下逻辑或者为什么Java要这样做吗?

4个回答

7
发生的情况是你的数字正在循环。更具体地说,你有一个数字30,000,在二进制中表示为:
0111 0101 0011 0000

当您将其加到自身并进位时,您会得到:
1110 1010 0110 0000

(注:在二进制中,将所有位向左移动一位就可以轻松地将数字乘以2。)
短整型是使用二进制补码表示的带符号数,这意味着最左边的1实际上是一个减号;该数字表示-5536。
如果你再将该数字乘以2,你需要超过2个字节才能表示它。由于你没有超过2个字节的short,当表达式的int结果缩小为short时,额外的位将被丢弃。重复这样做,你将会得到一个最左边为0的数;该数又变成了正数。然后最终你将再次得到一个最左边为1的数;该数又变成了负数。最终你将所有的0都移入该数中;任何整数乘以足够多次2都将导致0(具体来说,如果它是一个N位数,将其乘以2 N次总是会导致0)。
如果你不限制输入为short,并且需要的位数超过了int的最大值(33位或更多),那么你最终将会用尽可用位数,结果是多余的位数被丢弃,即整型溢出。如果任意一个参数是long类型,同样的情况也会发生,只不过需要65位或更多位。

不完全正确,这不是溢出而是缩小转换。该操作是在整数上执行的(没有溢出)- 然后通过删除最左边的位将结果转换为短整型。 - assylias
@assylias 很好的发现,我会进行修改。 - yshavit
毋庸置疑,这只是语义学问题,溢出的结果仍然相同;-)无论如何加1。 - assylias

4
首先,您的加法操作将short转换为int,因为加法运算符对操作数执行二进制数值提升
因此结果为int tmp = 60000; 然后通过缩小原始转换将该结果转换回short:

有符号整数到整型T的缩小转换仅丢弃除n个最低位以外的所有位,其中n是用于表示类型T的位数。

换句话说,60000 = 1110 1010 0110 0000b,但short是有符号的,所以初始的1是符号,并使用2的补码,您得到等效的short值,即-5536:1110 1010 0110 0000 => -0001 0101 1010 0000(取反所有位,加一并加上负号)

+1 表示 a+a 将会被执行为整数加法。 - Roddy of the Frozen Peas

3
    0111 0101 0011 0000
  + 0111 0101 0011 0000
  ______________________
    1110 1010 0110 0000 

Java中的short采用二进制补码编码。在二进制补码中,最高位被视为符号位,0表示正数,1表示负数。

1110 1010 0110 0000 = -5536 in two's complement

2
除非我的(和谷歌的)数学有误,否则这里没有第17位。 - yshavit
@yshavit:谢谢。我的直觉告诉我这是一个溢出问题。是我计算错误了。这只是一个简单的二进制补码编码。 - Chris Dargis

2
这与数据的二进制表示有关。在大多数系统中,使用了称为“二进制补码”的东西。正数只要有一个前导0,就会表现正常。
0010 = 2

将符号取反,将所有的0替换为1,并加上1:
-2 = 1110

如果我们将最大的正数,比如二进制中的01111加1会发生什么呢?我们得到了10000,这是一个负数(具体来说是Int.min_val)。当整数溢出时就会发生这种情况。

http://en.wikipedia.org/wiki/Two%27s_complement


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