sqrt() 是科學計算、圖形學、物理引擎中調用頻率最高的函數之一。本文追溯其算法演進:二分法 → 牛頓迭代法 → 快速逆平方根,揭示那個傳奇常數0x5f3759df背後的數學美學與工程智慧。
float SqrtByBisection(float n) {
if(n < 0) return n;
float low = 0, up = n, mid, last;
do {
mid = (low + up) / 2;
if(mid * mid > n) up = mid;
else low = mid;
} while(fabs(mid - last) > EPS);
return mid;
}
性能:收斂線性,需~30次迭代達到float精度,比庫函數慢數百倍。
求解 f(x) = x² - a = 0,切線迭代公式:
x_{n+1} = (x_n + a/x_n) / 2
float SqrtByNewton(float a) {
float x = a;
do {
float last = x;
x = (x + a/x) / 2;
} while(fabs(x - last) > EPS);
return x;
}
性能:誤差指數級減少,~5次迭代達到精度,但仍比庫函數慢數十倍。
float Q_rsqrt(float number) {
long i;
float x2 = number * 0.5F, y = number;
const float threehalfs = 1.5F;
i = *(long*)&y; // 1. 浮點位模式 → 整數
i = 0x5f3759df - (i >> 1); // 2. 神奇常數 + 移位 = 初始猜測
y = *(float*)&i; // 3. 整數 → 浮點位模式
y = y * (threehalfs - x2 * y * y); // 4. 一次牛頓迭代修正
return y; // 返回 1/√number
}
Float32:值 = (-1)^符號 × 2^(指數-127) × (1.尾數)
對數域近似:log₂(1/√x) ≈ -½ log₂(x)
整數解讀:I_x ≈ 2²³(log₂x + 127 - 0.5)
目標:I_y ≈ 2²³(127 - ½(log₂x + 127 - 0.5)) = 0x5f3759df - I_x/2
| 常數 | 來源 | 特點 |
|---|---|---|
| 0x5f3759df | John Carmack / Quake III | 工程經驗調優,極其優秀 |
| 0x5f37642f | Chris Lomont (Purdue) | 理論推導最優常數 |
| 0x5f375a86 | Lomont暴力搜索 | 實測誤差略小於Carmack版 |