我找到了这段代码:
int mid = (l & r) + ((l ^ r) >> 1)
与mid=(l+r)/2
但我无法理解为什么?
有任何帮助吗?谢谢!
答案 0 :(得分:3)
它不完全相同,重点不一样。它大多数相同,但没有溢出问题:如果你输入两个正数,结果永远不会是负数。 mid = (l + r) / 2
不是这样,如果你有l = 0x7fffffff, r = 1
,那么真正的中点是0x40000000
,但是天真的中点计算表明它是0xc0000000
,一个很大的负数。
添加可以分解为:
x + y = (x ^ y) + ((x & y) << 1)
这只是一个简单的“每位数的计算总和,然后单独应用进位”分解。然后将整个事物向右移动1,同时通过不向左移动开始并将另一个向右移动来恢复“从末端掉落”的位,
x + y = ((x ^ y) >> 1) + (x & y)
这是中点计算。请注意,它向向下舍入,而不是向零,这对于负面结果很重要。我不会将结果称为错误,它仍然位于端点之间,但它与正常签名除法的结果不匹配2(通常是向零舍入,尽管有关如何舍入的意见不同)。
您可以使用无符号右移来将其更改为适用于所有无符号整数:
// unsigned midpoint without wrapping/overflow
int mid = (l & r) + ((l ^ r) >>> 1);
当然是无符号中点,负输入被隐含地视为非常大的正数,这就是重点。
如果你正在使用有符号但非负数的数字(通常是中点计算的情况),你可以使用更简单的
int mid = (x + y) >>> 1