std::midpoint能防溢出,因其对整型同号时用a+(b-a)/2避免加法溢出,异号时用安全除法,浮点按ieee 754控制舍入;不适用于类型不匹配、自定义类型或中间值溢出场景。

std::midpoint 为什么能防溢出?
std::midpoint 不是简单算 (a + b) / 2,它内部对整型做了分支处理:当 a 和 b 同号时用位运算等价的 a + (b - a) / 2 形式,避免加法溢出;异号时直接走安全除法。浮点数则按 IEEE 754 规则做舍入控制,不引入额外误差。
常见错误现象:手写 (low + high) / 2 做二分查找时,low 和 high 都接近 INT_MAX,加法直接溢出为负数,导致下标越界或死循环。
使用场景集中在:
- 二分搜索的中点计算(尤其是数组索引、内存地址偏移)
- 区间插值、分段线性逼近
- 多线程中基于范围的任务切分(如
std::for_each_n分块)
注意:它只在 C++20 及以后可用,且对指针类型支持有限制——仅允许同数组内指针相减后参与计算,否则编译失败。
立即学习“C++免费学习笔记(深入)”;
怎么正确替换老代码中的中点计算?
不是所有 (a + b) / 2 都能无脑换。必须确认:
-
a和b类型一致,且是算术类型或同一数组内的指针 - 你不需要依赖溢出行为(比如某些 hack 式边界检测)
- 编译器已启用 C++20 或更高标准(
-std=c++20)
示例对比:
// 危险写法(可能溢出) int mid = (left + right) / 2; <p>// 安全写法(C++20) int mid = std::midpoint(left, right);</p>
如果 left 是 size_t,right 是 int,会编译失败——std::midpoint 要求类型严格匹配,不能隐式转换。此时得先统一类型,比如都转成 std::make_signed_t<size_t></size_t>(需确保值非负)。
和手动写 a + (b - a) / 2 比有啥区别?
手动写 a + (b - a) / 2 确实也能防整型加法溢出,但有几个坑:
- 对无符号类型(如
size_t),b - a在b 时会回绕,结果错误 - 对浮点数,
(b - a) / 2可能损失精度(尤其当a和b量级相差极大时) - 没有处理
NaN、无穷大等边界情况
std::midpoint 全部覆盖了这些:
- 整型:自动判断符号,选最稳路径
- 浮点:用
std::fma或等效逻辑保证舍入正确性 - 指针:只接受同数组内偏移,编译期就卡住非法用法
性能上几乎无开销——现代编译器对 std::midpoint 会内联并优化成和手写位运算等效的指令。
哪些情况它也救不了?
std::midpoint 不是万能数学保险丝:
- 输入本身已是溢出结果(比如从外部读入的非法
int值,本就不该参与计算) - 涉及自定义数值类型(如大整数类、定点数),它不重载,必须自己实现
- 多步计算中的中间值溢出(它只保“两点之间”,不保整个表达式)
最容易被忽略的一点:它不改变你算法本身的数值稳定性。比如在迭代求根时用 std::midpoint 算区间中点,但函数值计算仍可能因 double 精度崩掉——这时候得配合 std::fma、区间算术或更高精度库。
事情说清了就结束。









