[英]Why in Java (high + low) / 2 is wrong but (high + low) >>> 1 is not?
我理解>>>
修复了溢出:当添加两个大的正多头时,您最终可能会得到一个负数。 有人可以解释这种按位移位如何神奇地解决溢出问题吗? 它与>>
有何不同?
我的怀疑:我认为这与 Java 使用二进制补码的事实有关,所以如果我们有额外的空间,溢出是正确的数字,但因为我们没有它变成负数。 因此,当您移动并填充零时,由于二进制补码,它会神奇地固定。 但我可能是错的,有位大脑的人必须确认。 :)
简而言之, (high + low) >>> 1
是一个技巧,它使用未使用的符号位来执行非负数的正确平均。
在high
和low
都是非负的假设下,我们肯定知道最高位(符号位)为零。
所以high
和low
实际上都是 31 位整数。
high = 0100 0000 0000 0000 0000 0000 0000 0000 = 1073741824
low = 0100 0000 0000 0000 0000 0000 0000 0000 = 1073741824
当您将它们加在一起时,它们可能会“溢出”到顶部。
high + low = 1000 0000 0000 0000 0000 0000 0000 0000
= 2147483648 as unsigned 32-bit integer
= -2147483648 as signed 32-bit integer
(high + low) / 2 = 1100 0000 0000 0000 0000 0000 0000 0000 = -1073741824
(high + low) >>> 1 = 0100 0000 0000 0000 0000 0000 0000 0000 = 1073741824
作为有符号的 32 位整数,它会溢出并翻转为负数。 因此(high + low) / 2
是错误的,因为high + low
可能是负数。
作为无符号 32 位整数,总和是正确的。 所需要做的就是将其除以 2。
当然,Java 不支持无符号整数,因此我们必须除以 2(作为无符号整数)的最佳结果是逻辑右移>>>
。
在具有无符号整数的语言(例如 C 和 C++)中,它变得更加棘手,因为您的输入可以是完整的 32 位整数。 一种解决方案是: low + ((high - low) / 2)
最后列举>>>
、 >>
和/
之间的区别:
>>>
是逻辑右移。 它用零填充高位。>>
是算术右移。 它用原始顶部位的副本填充上部。/
是除法。数学上:
x >>> 1
将x
视为无符号整数并将其除以 2。 它四舍五入。x >> 1
将x
视为有符号整数并将其除以 2。 它向负无穷大舍入。x / 2
将x
视为有符号整数并将其除以 2。 它向零舍入。它对最高位进行零填充,而不是对它们进行符号填充。
int a = 0x40000000;
(a + a) / 2 == 0xC0000000;
(a + a) >>> 1 == 0x40000000;
我建议阅读 Joch Bloch 的http://googleresearch.blogspot.com/2006/06/extra-extra-read-all-about-it-nearly.html#!/2006/06/extra-extra-read- all-about-it-nearly.html关于高和低
“我为 JDK 编写的二进制搜索版本包含相同的错误。最近有人向 Sun 报告了它在等待九年左右后破坏了某人的程序。”
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.