今天刷到个算法视频,觉得很有意思,所以打算把它记录下来。
平方根倒数算法
- 源代码
- 二进制浮点数运算
- 对数技巧
- 平方根倒数
- 牛顿迭代
源代码
float Q_rsqrt(float number)
{long i;float x2, y;const float threehalfs = 1.5F;x2 = number * 0.5F;y = number;i = *(long *) &y; // evil float point bit hacki = 0x5f3759df - (i >> 1); // what the fuck?y = * (float *) &i;y = y * (threehalfs - (x2 * y * y)); // 1st iteration// y = y * (threehalfs - (x2 * y * y)); // 2nd iteration, can be removedreturn y;
}
二进制浮点数运算
-
参考标准 IEEE 754
-
计算机中 小数(浮点数)表示 由三部分二进制组成 : S 符号位 sign, E 指数偏移值 exponent bias, M 尾数位 Mantissa。以32为 浮点数 float 为例:
- 1位 符号位,8位指数位,23位有效数字位
- E ∈ ( − 127 , 128 ] , M ∈ [ 0 , 2 23 − 1 ] E \in (-127, 128], \ M\in[0, 2^{23}-1] E∈(−127,128], M∈[0,223−1]
- 其表示值 Value V 32 = S × 2 E − 127 × ( 1 + 1 2 23 M ) \begin{aligned}V_{32} &= S \times 2^{E - 127} \times (1 + \frac{1}{2^{23}}M) \end{aligned} V32=S×2E−127×(1+2231M)
-
拓展延伸
-
双精度 浮点数 double
V 64 = S × 2 E − 1023 × ( 1 + 1 2 51 M ) \begin{aligned}V_{64} &= S \times 2^{E - 1023} \times (1 + \frac{1}{2^{51}}M) \end{aligned} V64=S×2E−1023×(1+2511M)
-
尾数位(Mantissa),它的取值范围为 1 ~ 2 ,或者为 0~1。它也被称为分数值(fraction)、系数位(Coefficient),有效数字位(Significand)。
-
特殊数值:
- 0 : 0 00000000 0000000000000000000000
0 : 1 00000000 0000000000000000000000 - NaN:
- 指数位所有位都是1, 小数位只要不全为0,就表示非数值
- 例:0 11111111 11111111111100000010000
- Infinity:
- 符号位 0表示正无穷大,1表示负无穷大;
- 指数位 所有位都是1;
- 小数位所有位都是0。
- 例:0 11111111 00000000000000000000000
- 0 : 0 00000000 0000000000000000000000
-
类别 | 正负号 | 实际指数 | 有偏移指数 | 指数域 | 尾数域 | 数值 |
---|---|---|---|---|---|---|
零 | 0 | -127 | 0 | 0000 0000 | 000 0000 0000 0000 0000 0000 | 0.0 |
负零 | 1 | -127 | 0 | 0000 0000 | 000 0000 0000 0000 0000 0000 | −0.0 |
1 | 0 | 0 | 127 | 0111 1111 | 000 0000 0000 0000 0000 0000 | 1.0 |
-1 | 1 | 0 | 127 | 0111 1111 | 000 0000 0000 0000 0000 0000 | −1.0 |
最小的非规约数 | * | -127 | 0 | 0000 0000 | 000 0000 0000 0000 0000 0001 | ±2× 2= ±2≈ ±1.4×10 |
中间大小的非规约数 | * | -127 | 0 | 0000 0000 | 100 0000 0000 0000 0000 0000 | ±2× 2= ±2≈ ±5.88×10 |
最大的非规约数 | * | -127 | 0 | 0000 0000 | 111 1111 1111 1111 1111 1111 | ±(1−2) × 2≈ ±1.18×10 |
最小的规约数 | * | -126 | 1 | 0000 0001 | 000 0000 0000 0000 0000 0000 | ±2≈ ±1.18×10 |
最大的规约数 | * | 127 | 254 | 1111 1110 | 111 1111 1111 1111 1111 1111 | ±(2−2) × 2≈ ±3.4×10 |
正无穷 | 0 | 128 | 255 | 1111 1111 | 000 0000 0000 0000 0000 0000 | +∞ |
负无穷 | 1 | 128 | 255 | 1111 1111 | 000 0000 0000 0000 0000 0000 | −∞ |
NaN | * | 128 | 255 | 1111 1111 | non zero | NaN |
对数技巧
- 因为 平方根底数 为 正 S = + 1 S = +1 S=+1
- log 2 ( V 32 ) = log 2 ( 2 E − 127 × ( 1 + 1 2 23 M ) ) = E − 127 + log 2 ( 1 + 1 2 23 M ) ≈ E − 127 + 1 2 23 M + μ , log 2 ( 1 + x ) ≈ x + μ ≈ 1 2 23 ( M + 2 23 × E ) + μ − 127 \begin{aligned} \log_2 ( V_{32}) &= \log_2 \Big( 2^{E - 127} \times (1 + \frac{1}{2^{23}}M)\Big) \\ &= E - 127 + \log_2 (1 + \frac{1}{2^{23}}M) \\ &\approx E - 127 + \frac{1}{2^{23}}M + \mu \ , \ \log_2 (1+x) \approx x + \mu \\ & \approx \frac{1}{2^{23}}(M + 2^{23}\times E) + \mu - 127 \end{aligned} log2(V32)=log2(2E−127×(1+2231M))=E−127+log2(1+2231M)≈E−127+2231M+μ , log2(1+x)≈x+μ≈2231(M+223×E)+μ−127
- M + 2 23 × E = M + ( E < < 23 ) = M + 2^{23} \times E = M + (E << 23) = M+223×E=M+(E<<23)= (binary) V 32 V_{32} V32
平方根倒数
- 1 x = x − 1 2 \frac{1}{\sqrt{x}} = x ^{-\frac{1}{2}} x1=x−21
- log 2 ( 1 y ) = − 1 2 log 2 ( y ) = − ( log 2 ( y ) > > 1 ) \log_2(\frac{1}{\sqrt{y}}) = -\frac{1}{2} \log_2(y) = -(\log_2(y)>>1) log2(y1)=−21log2(y)=−(log2(y)>>1)
- Let log ( Γ ) = − 1 2 log ( y ) \log(\Gamma) = -\frac{1}{2} \log(y) log(Γ)=−21log(y)
- 1 2 23 ( M Γ + 2 23 × E Γ ) + μ − 127 = − 1 2 ( 1 2 23 ( M y + 2 23 × E y ) + μ − 127 ) ( M Γ + 2 23 × E Γ ) = 3 2 2 23 ( 127 − μ ) − 1 2 ( M y + 2 23 × E y ) = 0x5f3759df − ( y > > 1 ) \begin{aligned} \frac{1}{2^{23}}(M_\Gamma + 2^{23}\times E_\Gamma) + \mu - 127 &= -\frac{1}{2} (\frac{1}{2^{23}}(M_y + 2^{23}\times E_y) + \mu - 127) \\ (M_\Gamma + 2^{23}\times E_\Gamma) &= \frac{3}{2} 2^{23}(127 - \mu) - \frac{1}{2} (M_y + 2^{23}\times E_y) \\ &= \text{0x5f3759df} - (y >> 1) \end{aligned} 2231(MΓ+223×EΓ)+μ−127(MΓ+223×EΓ)=−21(2231(My+223×Ey)+μ−127)=23223(127−μ)−21(My+223×Ey)=0x5f3759df−(y>>1)
- 再把二进制整数 转换成浮点数,结果为近似值,需要用牛顿迭代减少误差。
牛顿迭代
y = 1 x → x = 1 y 2 y = \frac{1}{\sqrt{x}} \to x = \frac{1}{y^2} y=x1→x=y21
f ( y ) = 1 y 2 − x f(y)=\frac{1}{y^{2}}-x f(y)=y21−x
y new = y − f ( y ) f ′ ( y ) = y − 1 y 2 − x − 2 y 3 = 3 2 y − 1 2 x y 3 y_{\text {new }}=y-\frac{f(y)}{f^{\prime}(y)}=y-\frac{\frac{1}{y^{2}}-x}{-\frac{2}{y^{3}}}=\frac{3}{2}y-\frac{1}{2} x y^{3} ynew =y−f′(y)f(y)=y−−y32y21−x=23y−21xy3