Java, (low + high) >> 1 会溢出吗?

Java, will (low + high) >> 1 overflow?

我知道 >> 是有符号的,>>> 是无符号的

没有回答我问题的类似问题:

Java, will (low + high) >>> 1 overflow?

Safe integer middle value formula

根据第二个 link,他们为什么得出这样的结论: avg = (low & high) + ((low ^ high) >> 1); 会避免溢出吗?

为什么他们不能直接使用这个:(low + high) >> 1

(这用于 java 中的二进制搜索)

是的,如果总和足够高,(low + high) >> 1 可能会溢出。如您所知,>>> 用于无符号,因此它总是会在最重要的一侧移动 0。事实证明这非常重要,以防溢出。

使用 (low + high) 的诀窍是即使有溢出,信息仍然保留。如果确实溢出,最大可能的数学和仍然是 Integer.MAX_VALUE * 2,如果 Java 有一个,它仍然可以表示为无符号 int。但是我们可以和除以2时使用无符号右移运算符>>>处理为无符号整数。当按 1 向右移位时,这让我们将总和视为无符号 int、"un-overflowing" 和 int.

如果总和溢出,这里使用>>将不起作用,因为它会溢出成负数,而>>将移入1,保持负值。这会导致不正确的平均值计算(2 个正数的总和溢出将导致负平均值)。

无论您使用的是 >>> 还是 >>,溢出都是可能的。所以两者都可能溢出。但是只有>>>会处理好,妥妥的"un-overflowing"总和。

诀窍

avg = (low & high) + ((low ^ high) >> 1);

是求和可能溢出时计算平均值的另一种方法。这完全避免了溢出。这将总和分解为两部分——"carry" 位和 "non-carry" 位。

当使用加法时,结转到下一个更高有效位的位是当两个位都设置时 -- low & high。通常另外,这些位必须左移,但我们计算的是 2 个数字的平均值,所以我们最后也会右移。最终结果:此处没有偏移。

未携带的位要么是 1(如果位不同),要么是 0(如果位相同)——这就是 (low ^ high) 的来源(异或)。此外,通常这些位不会移动,但我们正在计算 2 个数字的平均值,所以我们最后也会右移。这种转变显示为 >> 1&^ 运算符不会溢出,因此 >> 在这里工作得很好。

示例:1100 (12) 和 1010 (10) 的平均值

1100 & 1010 = 1000
1100 ^ 1010 = 0110, 0110 >> 1 = 0011
1000 + 0011 = 1011 (11)