std::midpoint是c++20引入的安全计算中点函数,避免整数溢出和未定义行为,需包含、参数类型严格一致,不支持隐式转换。

std::midpoint 是 C++20 引入的、专为安全计算两数中点(即“平均值”)设计的标准函数,它能天然避免整数溢出,比 (a + b) / 2 可靠得多。
为什么 (a + b) / 2 在整数上会崩
对有符号整数,a + b 可能触发未定义行为(UB)——比如 INT_MAX + 1 不是简单的“变负”,而是编译器可随意优化或崩溃。无符号整数虽定义了回绕,但结果常不符合逻辑预期(如 0xFFFFFFF0U + 0x00000020U 得到一个极小值)。std::midpoint 绕开加法,用位运算或分段逻辑实现,全程不溢出。
常见错误现象:std::midpoint(0x7FFFFFFF, 0x7FFFFFFF) 返回 0x7FFFFFFF;而 (0x7FFFFFFF + 0x7FFFFFFF) / 2 触发 UB,实际运行可能 crash、返回负数,或被编译器优化掉整个分支。
- 只对同类型整数、浮点数、指针有效;不能混用
int和long - 对指针,
std::midpoint(p, q)要求p和q指向同一数组(或一前一后),否则行为未定义 - 浮点数版本不解决精度丢失,但避免了
inf或nan传播风险(例如std::midpoint(INFINITY, 0.0)是INFINITY,而加法先得inf再除 2 仍是inf,表面一样,但中间步骤更可控)
怎么用 std::midpoint 替代手写平均
直接替换即可,但要注意类型匹配和头文件:
立即学习“C++免费学习笔记(深入)”;
- 必须包含
<numeric></numeric>(不是<algorithm></algorithm>或<cmath></cmath>) - 参数类型必须严格一致:不能传
int和unsigned int,也不能传short和int(会编译失败) - 对自定义整型(如
int128_t),只要支持operator+、operator-和右移>>,通常也能用(依赖 ADL)
示例:
int a = INT_MAX; int b = INT_MAX - 1; auto m1 = std::midpoint(a, b); // OK: 结果是 INT_MAX - 1 auto m2 = (a + b) / 2; // UB:不要这么写
和 std::lerp 的区别在哪
std::midpoint(a, b) 等价于 std::lerp(a, b, 0.5),但后者是通用线性插值,接受任意 t 值,且对浮点数有额外精度控制(如使用 fma)。而 std::midpoint 是特化实现:整数路径完全不依赖浮点,也不调用 fma,性能略高,语义更清晰。
- 如果你只需要中点,用
std::midpoint—— 更轻、更安全、意图明确 - 如果需要
a + t*(b-a)形式的任意比例,才考虑std::lerp - 二者都不处理
NaN输入:传入NaN,结果仍是NaN,但不会 crash
真正容易被忽略的是:它不解决“类型隐式转换”问题。比如你写 std::midpoint(x, y),而 x 是 int16_t、y 是 int32_t,编译直接报错。这时候得手动统一类型,而不是指望它自动提升——它的安全,是以类型严格为前提的。