std::midpoint是C++20中唯一标准定义不溢出的中点计算工具,但仅对同类型有符号整型和指针安全;混用类型、误用于浮点或无效区间将引发编译错误或逻辑bug。

std::midpoint 是 C++20 中唯一被标准明确定义「不引发整数溢出」的中点计算工具,但它不是万能胶——用错类型、忽略前提、硬套浮点场景,反而会引入隐蔽 bug。
std::midpoint(int, int) 能防溢出,但只在类型匹配且为有符号整型时最可靠
它内部对同号整数走 a + (b - a) / 2 路径,避免加法峰值;对异号则切换安全除法。但这要求两个参数必须是同一有符号整型(如都是 int 或都是 long long):
-
std::midpoint(10, 20)✅ 安全,结果为15 -
std::midpoint(INT_MAX, INT_MAX - 1)✅ 不溢出,结果正确 -
std::midpoint(10U, 20)❌ 编译失败:无符号与有符号混用 -
std::midpoint(-5, 2)✅ 返回-2(向下取整语义,即floor((a+b)/2)) - 若你传的是
size_t,而值可能接近SIZE_MAX,需确认业务是否接受模算术语义——std::midpoint对无符号类型也合法,但b - a回绕后结果仍是数学中点(模意义下)
std::midpoint(T*, T*) 是指针算术唯一真正不可替代的用途
这是它比手写 begin + (end - begin) / 2 更安全的核心场景:当数组极大(比如接近地址空间上限),end - begin 可能超出 ptrdiff_t 表示范围,导致未定义行为;而 std::midpoint 要求实现规避该问题。
-
std::midpoint(arr, arr + N)✅ 安全,只要arr和arr + N指向同一数组(含 one-past-the-end) -
std::midpoint(v.begin(), v.end())❌ 不合法:迭代器不是原生指针;得用std::midpoint(std::data(v), std::data(v) + v.size()) -
std::midpoint(ptr1, ptr2)要求ptr2可从ptr1到达(即ptr1 ),否则行为未定义 - 不支持
void*、const T*与T*混用——类型必须完全一致
float/double 版本存在,但别指望它防溢出或提精度
C++20 确实提供了 std::midpoint(float, float) 重载,但它只是语法存在,底层仍可能调用 (a + b) / 2,不保证规避浮点溢出或精度损失。
立即学习“C++免费学习笔记(深入)”;
- 当
a = 1e30,b = 1.0时,a + b可能直接舍入为a,导致结果错误;std::midpoint并不强制改用std::fma或量级判断 - 它不处理
NaN、inf的传播逻辑,行为与平台fadd/fdiv一致 - 真要抗浮点溢出,应手动分支:
if (std::abs(a) > std::abs(b)) { return a * 0.5 + b * 0.5; } else { return b * 0.5 + a * 0.5; },或用std::fma(a, 0.5, b * 0.5) - 编译器不会因用了
std::midpoint就自动优化掉浮点风险——它不提供额外保障
替换老代码前必须检查三个硬约束
不能把所有 (a + b) / 2 一键替换成 std::midpoint(a, b),否则编译失败或语义偏移:
- 必须启用 C++20:GCC/Clang 加
-std=c++20,MSVC 用/std:c++20 - 两个参数类型必须严格相同——
std::midpoint(1, 1L)报错,得写成std::midpoint(1L, 1L) - 不能依赖“溢出后截断”这类未定义行为做 hack(比如某些边界检测逻辑),
std::midpoint会打破它 - 它不校验业务有效性:二分查找中若
left > right,它仍返回一个数学上合法的整数,但你的算法可能已崩了
最容易被忽略的一点:std::midpoint 只解决「给定两个合法值,如何算中点不溢出」这一个问题。它不管这两个值是不是有效索引、区间是否为空、指针是否越界——那些仍是你的责任。


















