为什么在Java(高+低)/ 2错误但(高+低)>>> 1不是?
我理解>>>
修复溢出:当添加两个大的正长数时,你可能会得到一个负数。 有人可以解释这种按位转换如何神奇地修复溢出问题吗? 它与>>
什么不同?
我怀疑:我认为这与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-关于高低的所有关于它的几乎所有
“我为JDK编写的二进制搜索版本包含了同样的错误。最近有人向Sun报告,在等待了9年左右之后它破坏了某人的程序。”