std::midpoint 比 (a + b) / 2 更安全,因为它避免有符号整数加法溢出导致的未定义行为,通过位运算或条件逻辑实现向零取整的中点计算,且对整型、指针、浮点类型重载,不支持自定义类型。

std::midpoint 为什么比 (a + b) / 2 更安全
因为 a + b 在有符号整数上可能溢出,触发未定义行为;而 std::midpoint 是专门设计来避免这个问题的——它不依赖加法,而是用位运算或条件逻辑等价实现「向零取整的中点」。
比如 int a = INT_MAX, b = 1;,(a + b) / 2 会溢出,结果不可预测;但 std::midpoint(a, b) 稳定返回 INT_MAX / 2 + 1(即 1073741824)。
- 只对整型、指针、浮点类型重载,不支持自定义类型
- 对整型:语义等价于
(a & b) + ((a ^ b) >> 1)(无符号逻辑),但标准不规定具体实现 - 对指针:要求两指针指向同一数组(或一前一后一个位置),否则行为未定义
- 浮点类型下仍可能有舍入误差,但不会溢出(如
midpoint(1e308, 1e308)安全,而1e308 + 1e308会得inf)
什么时候必须用 std::midpoint 而不是手写公式
二分查找边界更新、内存地址中点计算、安全插值场景下,只要输入可能达到类型极限,就该用 std::midpoint。
典型错误是写 low + (high - low) / 2 ——这虽能防溢出,但对负数有偏差:std::midpoint(-1, 0) 是 -1(向零取整),而 -1 + (0 - (-1)) / 2 也是 -1;但 std::midpoint(-3, 2) 是 -1,而手写公式在有符号除法截断规则下也可能不一致。
立即学习“C++免费学习笔记(深入)”;
- 使用
std::midpoint前确认编译器支持:C++20 起引入,GCC 10+、Clang 11+、MSVC 19.28+ 支持 - 若需兼容旧标准,可用
static_cast<long long>(a) + (static_cast<long long>(b) - a) / 2</long></long>,但需确保long long足够宽 - 对指针,别传
nullptr和非关联地址,例如std::midpoint(p, q)中p和q必须满足p 且在同一对象内
std::midpoint 的常见误用和报错
最常遇到的是编译失败或链接错误,本质是没开 C++20 或忘了包含头文件。
错误信息类似:error: 'midpoint' is not a member of 'std' 或 undefined reference to 'std::midpoint'。
- 必须
#include <utility></utility>(不是<algorithm></algorithm>或<numeric></numeric>) - 编译时加
-std=c++20(GCC/Clang)或/std:c++20(MSVC) - 不能对
char*字面量直接调用,比如std::midpoint("abc", "def")不合法——它们不是同一数组的指针 - 对浮点类型,传
NaN会导致返回NaN,但不会崩溃;不过这不是“错误”,而是符合 IEEE 754 的预期行为
性能和可读性权衡:真有必要换吗
在绝大多数普通业务代码里,不用 std::midpoint 也不会出事;但一旦涉及底层算法、系统库、竞赛代码或处理用户可控的大整数,它就是一道低成本的安全护栏。
生成的汇编通常比手写公式更短——比如对 32 位整型,std::midpoint 常被优化成 2–3 条位指令,而 static_cast<long long></long> 方案要扩位、运算、再缩回,开销更大。
- 不要为了“看起来现代”而强行替换已验证正确的旧代码
- 新写的二分逻辑、索引计算、buffer 分割,优先考虑
std::midpoint - 注意:它不解决所有数值问题,比如
std::midpoint(0, 3)是1(向下取整),不是1.5;需要浮点中点仍得自己转类型
真正容易被忽略的是指针版本的约束条件——两个地址必须可比较且属于同一内存块,这个检查没法在编译期做,运行时越界或未定义行为很难调试。










