std::midpoint仅对同类型整数和同数组指针防溢出,整数中点向下取整且支持负数跨零;浮点插值应避免使用,需用fma或条件分支;指针中点适用于大数组分块但不校验有效性。

物理引擎里直接用 (a + b) / 2 算位置或速度中点,只要坐标/位移接近 INT_MAX 或 SIZE_MAX(比如超大世界、长时间累积),加法就溢出——结果不是错一点,而是突变、碰撞失效、模拟崩溃。但 std::midpoint 并不能通吃所有插值场景:它只对同类型整数和同数组指针真正防溢出;对 float/double 位置插值,它等价于裸写 (a + b) / 2,不防溢出、不提精度,还可能掩盖问题。
std::midpoint(int, int) 是离散格点坐标的唯一安全中点方案
刚体在体素世界、网格化碰撞盒、索引化空间划分中若用 int 表示格点坐标,std::midpoint 是目前 C++ 标准中唯一保证不触发加法溢出的中点计算方式:
- 必须两个参数为同一有符号整型,例如
std::midpoint(pos_a, pos_b)中两者都为int;传int和long会编译失败 - 结果是向下取整中点:
std::midpoint(-3, 2)返回-1,不是0——这对格点对齐、AABB 边界判定是关键语义 - 支持负数、跨零区间,无需分支判断符号;但若
pos_b < pos_a(如反向区间),结果仍数学正确,业务层需自行校验是否允许该方向 - 不兜底表达式级溢出:比如
std::midpoint(x, y + 1)中y + 1先溢出,std::midpoint不管——它只保自身算术过程安全
float/double 位置插值千万别用 std::midpoint
物理引擎绝大多数状态量(位置、速度、加速度)用 float 或 double,此时调用 std::midpoint(a, b) 完全没意义:
-
std::midpoint(1e30f, 1.0f)极大概率返回1e30f,因为1e30f + 1.0f == 1e30f,加法丢失低位;而手写a + (b - a) * 0.5f至少保留相对误差可控 - 没有
NaN/inf特殊处理,和裸算一样传播异常值;某帧位置因数值不稳定产生inf,std::midpoint照常返回inf - 真正需要稳健浮点中点时,应优先用
std::fma(a, 0.5f, b * 0.5f)(需#include <cmath>),或根据量级分支:std::abs(a) >= std::abs(b) ? a + (b - a) * 0.5f : b + (a - b) * 0.5f
std::midpoint(ptr, ptr) 是大规模并行内存切分的不可替代方案
当物理引擎做多线程刚体更新、BVH 构建或大数组分块时,需把连续内存均分给 worker 线程——这是 std::midpoint 唯一无法被手写替代的刚需场景:
立即学习“C++免费学习笔记(深入)”;
-
std::midpoint(begin_ptr, end_ptr)明确要求实现规避end_ptr - begin_ptr超出ptrdiff_t范围的未定义行为,哪怕数组长度逼近SIZE_MAX / sizeof(RigidBody)也安全 - 手写
begin + (end - begin) / 2在极端大数组下,end - begin可能溢出ptrdiff_t,导致未定义行为 - 必须指向同一数组(或 one-past-the-end),不支持
void*、不支持const int*和int*混用;std::vector::iterator不支持,得转成&v[0]或用std::data(v)
最常被忽略的是:它只解决「算术过程不溢出」,不解决「业务逻辑是否合理」——传进去两个悬空指针,它也可能“成功”返回地址;传进去越界的 arr + N,它不校验有效性。安全边界仅限数值/指针算术本身,不在语义层兜底。


















