我理解>>>
修复了溢出:当添加两个大的正长数时,你可能会得到一个负数。有人可以解释这种按位移位如何神奇地修复溢出问题吗?它与>>
的区别如何?
我的怀疑:我认为这与Java使用两个赞美这一事实有关,所以如果我们有额外的空间但溢出是正确的数字但是因为我们没有它变成负面。所以当你移动并用零划桨时,它会因为两个赞美而神奇地固定下来。但我可能是错的,有点大脑的人必须证实。 :)
答案 0 :(得分:51)
简而言之,(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。它向零舍入。答案 1 :(得分:11)
它填充最顶部的位而不是填充符号。
int a = 0x40000000;
(a + a) / 2 == 0xC0000000;
(a + a) >>> 1 == 0x40000000;
答案 2 :(得分:4)
我建议阅读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报告了它破坏某人的错误 程序,等待了九年左右。“