是的,如果总和足够高,(low + high) >> 1 可能会溢出。如您所知,>>> 是无符号的,因此它总是会在最重要的一侧移入0。事实证明这非常重要,以防它溢出。
使用(low + high) 的诀窍是即使溢出,信息仍会保留。如果它确实溢出,最大可能的数学和仍然是Integer.MAX_VALUE * 2,如果Java 有一个,它仍然可以表示为无符号int。但是,当我们使用无符号右移运算符>>> 除以 2 时,我们可以将和视为一个无符号整数。当向右位移1 时,这让我们将总和视为无符号int,“未溢出”int。
如果总和溢出,则在此处使用>> 将不起作用,因为它会溢出为负数,而>> 将移入1,使值保持为负。这会导致错误的平均值计算(2 个正数,其总和溢出将导致负平均值)。
无论您使用的是>>> 还是>>,都有可能溢出。所以两者都可以溢出。但只有>>> 能很好地处理此案,正确地“不溢出”总和。
诀窍
avg = (low & high) + ((low ^ high) >> 1);
是在总和可能溢出时计算平均值的另一种方法。这完全避免了溢出。这将总和分为两部分——“进位”位和“非进位”位。
使用加法时,转移到下一个更高有效位的位是两个位都设置时 -- 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)