无符号右移能避开溢出是因为它将32位整数视为纯数值,高位补0,使溢出后的负数经>>>1仍得正确中点;如(-2)>>>1=2147483647。

无符号右移为何能避开溢出
在 Java、C++ 等有符号整型语言中,(left + right) / 2 的问题出在加法阶段:两个大正数相加后,二进制结果超过 32 位有符号范围,符号位被置为 1,整个值变成负数。而 >>>(无符号右移)不看符号位,它把整个 32 位当作纯数值处理,高位一律补 0。哪怕 left + right 已经溢出成负数,用 >>> 对它右移 1 位,等价于把它当无符号整数除以 2,结果依然正确。
正确写法与注意事项
安全的中间索引计算应写成:
- Java/C++:mid = (left + right) >>> 1(注意:>>> 优先级低于 +,括号不可省)
- 该写法在 left 和 right 均为 int 且接近 Integer.MAX_VALUE 时仍可靠,例如 left = 2147483647, right = 2147483647 → (left + right) 溢出为 -2,但 (-2) >>> 1 = 2147483647,正是期望的中点
- 不要混用:(left + right) >> 1 仍有风险,因为 >> 是有符号右移,对溢出后的负数会高位补 1,导致错误结果
- Python 不需要此写法(整数无限精度),但为跨语言一致性,建议统一使用 left + (right - left) // 2 更直观
和减法防溢写法的对比
两种主流防溢方式本质不同:
- left + (right - left) / 2:数学等价变形,完全规避加法,逻辑清晰,所有语言通用
- (left + right) >>> 1:依赖底层位操作语义,高效但需语言支持(Java 有,C/C++ 无原生 >>>,需转 unsigned int 处理)
- 实际工程中,前者更易读、易审查、易移植;后者在 Java 高性能场景可作为备选,但团队规范通常优先推荐前者
验证是否真正生效
写个最小测试即可确认:
- 在 Java 中运行:int left = Integer.MAX_VALUE - 1; int right = Integer.MAX_VALUE; int mid = (left + right) >>> 1; —— 结果应为 2147483647
- 对比错误写法:int midBad = (left + right) / 2; —— 结果是负数,直接暴露问题
- 单元测试应覆盖 left 和 right 同时 ≥ 2147483640 的用例,这是溢出高发区间











