不能直接用1.0/sqrt(x)是因为标准sqrt开销大,而游戏引擎等需高频单位化向量;0x5f3759df是IEEE 754单精度下牛顿迭代最优初值拟合常数,仅适用于float且x>0,现代编译器优化后通常无需手写该算法。

为什么不能直接用 1.0 / sqrt(x)
因为标准库的 sqrt 依赖硬件浮点指令或软件实现,开销大;而游戏引擎(如早期《雷神之锤III》)需要每帧对成千上万个向量做单位化——1.0 / sqrt(x) 会成为瓶颈。关键不是“不准”,而是“太慢”。现代 CPU 虽已优化 sqrt,但逆平方根仍有低延迟需求场景,比如实时物理模拟中的归一化方向向量。
0x5f3759df 是怎么来的?它不是 magic number 而是拟合解
这个常数源于对函数 f(y) = 1/√x − y 在 IEEE 754 单精度浮点数的位表示空间中,用牛顿迭代初值逼近时的最优线性近似截距。它不是拍脑袋写的,而是通过最小化最大误差反推出来的拟合参数(后来更优版本是 0x5f375a86)。直接抄常数能跑,但若你改用双精度、或想移植到非 IEEE 平台,必须重新拟合。
常见错误:把该算法套在 double 上——它只对 float 有效,因为位操作基于 32 位整数解释。
使用场景:x > 0 且不追求极高精度(相对误差约 0.17%),适合做一次牛顿迭代前的粗略初值。
立即学习“C++免费学习笔记(深入)”;
标准实现里为什么要写两次牛顿迭代?
原始算法只做一次迭代(y = y * (1.5f - x * 0.5f * y * y)),但现代编译器和 CPU 浮点单元性能提升后,**一次迭代已足够用于多数图形计算**;二次迭代纯属历史惯性,反而可能因额外乘加引入误差累积(尤其当 x 接近 0 或极大时)。是否迭代、迭代几次,应按实际误差容忍度测。
实操建议:
- 只在
float类型、x ∈ [0.01, 100]区间内信任该算法 - 避免对
x == 0或负数调用——它不会报错,但结果无意义(返回极大正数或 NaN) - 若需双精度,请用
rsqrtss指令(x86 SSE)或std::numbers::inv_sqrt2(C++20 起部分实现支持)
float fast_inv_sqrt(float x) {
float xhalf = 0.5f * x;
int i = *(int*)&x; // 位 reinterpret
i = 0x5f3759df - (i >> 1); // 神奇移位与减法
float y = *(float*)&i; // 重解释回 float
y = y * (1.5f - xhalf * y * y); // 一次牛顿迭代
return y;
}
现代 C++ 里其实有更安全又不慢的选择
Clang/GCC 在 -ffast-math 下会自动将 1.0f / sqrtf(x) 优化为 rsqrtss 指令(x86)或等效 NEON 指令(ARM),且带硬件级误差控制;MSVC 启用 /arch:AVX2 也类似。这意味着:手写位操作不仅可读性差、不可移植,还可能比编译器生成的汇编更慢。
真正需要自己实现的,只有两类情况:目标平台无硬件 rsqrt(如某些嵌入式 DSP)、或你在写一个不依赖编译器优化的底层数学库。否则,优先写 1.0f / sqrtf(x) 并开对应编译选项。
最容易被忽略的一点:该算法对 denormalized float(次正规数)完全失效,而现代 GPU 渲染管线默认 flush-to-zero,CPU 则可能因 MXCSR 设置不同导致行为突变——这点连原版 Quake III 源码注释都没提。


















