std::midpoint仅对整型和指针安全防溢出,因采用a + (b - a) / 2避免和溢出;浮点数上不解决精度与溢出问题,且要求参数类型严格一致、同为有符号整型时b≥a最稳妥。

std::midpoint 能安全算中点,但只在整型和指针上真正防溢出;对 float/double 它不解决精度或溢出问题,反而可能更糟。
std::midpoint 为什么能防整数溢出
它不依赖 a + b,而是等价于 a + (b - a) / 2 —— 差值 b - a 永远不会比 a 或 b 更容易溢出(尤其对有符号整型)。比如 std::midpoint(INT_MAX, INT_MIN) 返回正确结果,而 (INT_MAX + INT_MIN) / 2 是未定义行为。
内部实现可能用位运算或条件分支,但你不需要关心;只要类型匹配、编译器支持 C++20,就能拿到标准保证的无溢出结果。
- 仅当
a和b同为有符号整型且b >= a时,行为完全定义且最稳妥 -
a > b也合法,结果仍是floor((a + b) / 2),比如std::midpoint(5, 1) == 3 - 对
unsigned类型,b - a会回绕(wrap around),语义上是模算术中点,需你自己确认是否符合业务预期
哪些类型能用,哪些会编译失败
必须包含 #include <numeric></numeric>,且启用 C++20(如 -std=c++20)。函数模板严格要求两个参数类型完全一致:
立即学习“C++免费学习笔记(深入)”;
- ✅ 支持:
int、long long、size_t、同类型指针(如int*) - ❌ 编译失败:
std::midpoint(1, 1U)(intvsunsigned int)、std::midpoint(1.0f, 2.0f)(C++20 标准未定义浮点重载,部分编译器虽允许但不推荐) - ⚠️ 迭代器不行:
std::midpoint(v.begin(), v.end())不合法,因为std::vector::iterator不是原生指针;得写成std::midpoint(std::data(v), std::data(v) + v.size())
指针中点是唯一不可替代的用途
这是 std::midpoint 最硬核、最不可替代的场景:计算大数组边界指针的中点,避免 end - begin 超出 ptrdiff_t 范围导致未定义行为。
例如:
int arr[SIZE_MAX / sizeof(int) + 1]; int* begin = arr; int* end = arr + SIZE_MAX / sizeof(int); // end 几乎到地址上限 auto mid = std::midpoint(begin, end); // 安全,标准强制要求实现规避指针算术溢出
- 只接受相同 cv 修饰的同类型指针,比如
const int*和int*混用会编译失败 - 不要传
void*或跨数组指针,行为未定义 - 它不检查
begin和end是否属于同一数组,也不验证非空——这些仍是你的责任
浮点数上别迷信 std::midpoint
对 float 或 double,std::midpoint 并不比 (a + b) * 0.5 更安全。标准没要求它规避浮点溢出或精度损失;某些实现甚至直接退化为 (a + b) / 2。
- 当
a = 1e30、b = 1.0时,a + b可能直接舍入为a,结果错误;而手写a + (b - a) * 0.5或用std::fma(a, 0.5, b * 0.5)更可控 - 若真需要抗溢出浮点中点,优先考虑分量判断量级 + 手动分支,而不是默认套用
std::midpoint - C++20 中
std::midpoint(float, float)是合法重载,但只是“语法存在”,不是“语义保障”
真正容易被忽略的是:它只管“给两个合法值,怎么算中点最安全”,不管上下文是否合理——比如二分查找里传了空区间、指针越界、或负索引,std::midpoint 本身不报错,也不会帮你兜底。


















