位移操作未返回预期结果。

6

当我对1 << 63进行位移时,为什么Java会返回-2147483648

预期结果应该是9 223 372 036 854 775 808,使用 Wolfram Alpha 和我的计算器进行测试。

我进行了如下测试:

System.out.print((long)(1 << (63)));
2个回答

28

这一行有一个重要的注意事项。

System.out.print((long)(1 << (63)));

你首先要取 (1 << 63),然后将其转化为 long 类型。因此,实际上你是在整数中进行左移操作,所以 long 转换没有任何效果。这就是为什么将 63 比特左移得到的是最小整数而不是最小 long 的原因。

但还有一个更重要的点,Java 的 long 类型始终是有符号的,所以即使是以下这行代码

System.out.print(1L << 63);

如果使用二进制补码,若最左边的位是1,则会得到一个负数。

实际上,你无法用Java原始类型表示数字263 = 9223372036854775808,因为这个数字比最大的long类型还要大,而long是最大的原始类型。不过,你可以用BigInteger表示这个数字。你甚至可以通过将63左移来生成它的代码:

BigInteger.ONE.shiftLeft(63)

对于一个很好的答案,如果它还包括实用的解决方案,请点赞。顺便说一句,我希望你不介意我修改了2^63,使它更易读。 - amit
当然,您可以将2 ^ 63表示为long类型,但某些操作会将其解释为负数,但对于大多数数学运算而言,符号无关紧要。 - harold

6

您正在遭遇 整数溢出 [两次]。

1 << 32 == 1
1 << 31 == -2147483648 [ becuase this is the binary representation in 2's complement for -2147483648]
1 << 63 == 1 << (32 + 31) == (1 << 32) << 31 == 1 << 31 == -2147483648

当你执行(long)(1 << (63))时,你只是将1 << (63)的结果[即-2147483648]转换为long类型 - 但它并不改变其值。

感谢您指出并描述了溢出问题!我一直在寻找对OpenJDK的BitSet实现中发现的这段代码的解释:[ long firstWordMask = WORD_MASK << fromIndex; long lastWordMask = WORD_MASK >>> -toIndex; ] 即使在这种情况下WORD_MASK是long类型。 - ruizpauker

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