std::midpoint是C++20唯一保证不触发有符号整数加法溢出的中点计算工具,底层用a+(b-a)/2避免溢出,支持同类型整数和原生指针,但不适用于浮点数或混合类型。

直接说结论:std::midpoint 是 C++20 唯一被标准保证「不触发有符号整数加法溢出」的中点计算工具,但它不是万能平均函数——用错类型、传浮点、混用符号类型,都会让它失效甚至编译不过。
为什么 (a + b) / 2 在整数上会崩
这不是“偶尔出错”,而是未定义行为(UB):当 a 和 b 同号且绝对值足够大时,a + b 直接溢出。比如 INT_MAX 和 1 相加,结果不可预测,优化器可能直接删掉整段逻辑。
std::midpoint 底层等价于 a + (b - a) / 2(对有符号整数),全程避开加法峰值。它不依赖 + 操作符做中间求和,而是靠位运算或无溢出减法+除法组合实现。
- 对
int:支持std::midpoint(INT_MIN, INT_MAX)→ 返回-1(正确中点) - 对
unsigned int:减法自动回绕,语义上仍是模意义下的中点,但你要清楚自己是否需要这个语义 - 别信网上抄来的
(a & b) + ((a ^ b) >> 1)—— 它对负数完全错误,且不处理符号扩展
std::midpoint(int, int) 的实际行为细节
它的返回值是向下取整的数学中点,即 ⌊(a+b)/2⌋,不是四舍五入,也不是向零取整。
-
std::midpoint(-3, 2)==-1(不是 0) -
std::midpoint(0, 1)==0(整型返回,不带小数) -
std::midpoint(5, 1)==3(允许a > b,结果仍合法) - 参数类型必须严格一致:
std::midpoint(10, 20U)编译失败;std::midpoint(size_t{1}, ptrdiff_t{2})也失败
指针中点:唯一不可替代的用途
这是 std::midpoint 真正不可替代的场景——安全计算大数组内两个指针的中间地址,且规避 ptr_diff 超出 ptrdiff_t 表示范围的风险。
- 必须包含
,且启用-std=c++20 - 只接受同类型原生指针:
int*和const int*混用会编译失败(除非显式const_cast,但不推荐) - 必须指向同一数组(或其中一个是
&arr[N]);跨数组、nullptr、悬空指针 → 未定义行为,编译器不报错 -
std::midpoint(v.begin(), v.end())❌ 不行,迭代器不是原生指针;要写成std::midpoint(&v[0], &v[0] + v.size())✅
浮点数上用 std::midpoint 就是自找麻烦
C++20 标准确实为浮点提供了 std::midpoint(float, float) 重载,但它只是语法存在,**不提供任何安全优势**,反而可能引入精度偏差。
- 它底层就是调用
(a + b) / 2,不规避浮点溢出或量级失配问题(如1e30f + 1.0f→ 仍为1e30f) - 若真需抗溢出浮点中点,优先用
std::fma(a, 0.5, b * 0.5)或手动分支判断量级 -
std::lerp(a, b, 0.5)更接近“中点”语义,且在端点(t=0/1)有精度保障,但它不是中点函数,别混淆用途 - 别把
std::midpoint当通用平均工具——它专为二分索引、内存布局、指针算术这类需要严格中点语义的底层场景设计
最容易被忽略的点:它不检查区间是否为空、不验证指针有效性、不处理跨类型转换。你传进去什么,它就按规则算什么——错在调用方,不在函数本身。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











