对于快速计算出平方根倒数 1 x \frac{1}{\sqrt{x}} x 1 ,有一个上世纪90年代的经典算法:
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 floating point bit level hacking
i = 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, this can be removed
return y;
}
扫盲:浮点数在计算机中的储存
float32和double64都由 IEE754 标准定义,在这里只简单了解float32。
在这32位中分为三个部分:
Sign 符号:0代表整数,1代表负数
Exponent 指数位
二进制的8bit可以表示256种状态,而IEE754规定,这八位用于表示[-127, 127]范围内的指数。
为了方便地表示负数,其规定在float32中指数的偏移量为127,因此指数的实际值为e = exponent - 127。
这将确保以二进制储存的biased exponent总是非负整数。
尾数位在格式中有23位,因此可以表示的精度为2^-23,约等于1.19 * 10^-7。
如有一个十进制数13.62,将整数部分除2取余逆序排列为1101,将小数部分乘2取整顺序排列为101。这个十进制小数在二进制的表示为1101.101,即1.101101 * 2^3。
将指数部分加上偏移量127,得到130,则exponent部分的值为10000010。
因为规定了小数的最高位总是非零数,所以在二进制中它总是为1,因此在尾数部分可以省略最高位的储存,只考虑小数点后面的数字。如凑不够23位则低位补零。
value = ( − 1 ) sign × 2 ( E − 127 ) × ( 1 + M 2 23 ) {\text{value}}=(-1)^{\text{sign}}\times 2^{(E-127)}\times \left(1+\frac{M}{2^{23}}\right) value = ( − 1 ) sign × 2 ( E − 127 ) × ( 1 + 2 23 M )
其中,M 2 23 \frac{M}{2^{23}} 2 23 M 将尾数 M M M 归一化到范围 [ 0 , 1 ) [0, 1) [ 0 , 1 ) 。
牛顿迭代法求根
对于 f ( x ) = 0 f(x)=0 f ( x ) = 0 有一个近似解 x n x_n x n ,通过给出的根得到更近似的根。
x n + 1 = x n − f ( x n ) f ′ ( x n ) x_{n+1} = x_n - \frac{f(x_n)}{f'(x_n)} x n + 1 = x n − f ′ ( x n ) f ( x n )
已经证明牛顿迭代法的二次收敛条件:
f ′ ( x ) ≠ 0 {\displaystyle f'(x)\neq 0} f ′ ( x ) = 0 ; x ∈ I {\displaystyle x\in I} x ∈ I ,其中 I {\displaystyle I} I 为区间[ α − r , α + r ] [α − r, α + r] [ α − r , α + r ] ; f ( x ) {\displaystyle f(x)} f ( x ) 在 I {\displaystyle I} I 上连续可微; 初始根 x 0 x_0 x 0 足够接近根 x ∗ x_* x ∗
最开始的浮点数公式:
x = ( 1 + M 2 23 ) 2 ( E − 127 ) x=\left(1+\frac{M}{2^{23}}\right) 2^{(E-127)} x = ( 1 + 2 23 M ) 2 ( E − 127 )
两边同取对数,
log 2 ( x ) = log 2 ( ( 1 + M 2 23 ) 2 ( E − 127 ) ) = ( E − 127 ) + log 2 ( 1 + M 2 23 ) \begin{align}
\log_{2}(x) &= \log_{2}(\left(1+\frac{M}{2^{23}}\right) 2^{(E-127)}) \\
&= \left(E-127\right)+\log_{2}(1+\frac{M}{2^{23}})
\end{align} log 2 ( x ) = log 2 ( ( 1 + 2 23 M ) 2 ( E − 127 ) ) = ( E − 127 ) + log 2 ( 1 + 2 23 M )
在x ∈ [ 0 , 1 ] x \in [0, 1] x ∈ [ 0 , 1 ] ,log 2 ( 1 + x ) ≈ x \log_{2}(1+x) \approx x log 2 ( 1 + x ) ≈ x ,y = log 2 ( 1 + x ) y=\log_{2}(1+x) y = log 2 ( 1 + x ) 与 y = x y=x y = x 在图像上十分接近。
log 2 ( x ) = ( E − 127 ) + log 2 ( 1 + M 2 23 ) = M 2 23 + E − 127 = M + 2 23 × E 2 23 − 127 \begin{align}
\log_{2}(x) &= \left(E-127\right)+\log_{2}(1+\frac{M}{2^{23}})\\
&= \frac{M}{2^{23}}+E-127 \\
&= \frac{M+2^{23} \times E}{2^{23}}-127
\end{align} log 2 ( x ) = ( E − 127 ) + log 2 ( 1 + 2 23 M ) = 2 23 M + E − 127 = 2 23 M + 2 23 × E − 127
此时,M + 2 23 E M+2^{23}E M + 2 23 E 就是浮点数在二进制中的表达,其中 2 23 2^{23} 2 23 使 E E E 在二进制中向左移23位。
l o g 2 ( x ) = M + 2 23 × E 2 23 − 127 log_{2}(x) = \frac{M+2^{23} \times E}{2^{23}}-127 l o g 2 ( x ) = 2 23 M + 2 23 × E − 127 其实就是浮点数 x x x 与其在二进制中的关系
设有解 a = 1 y a=\frac{1}{\sqrt{y}} a = y 1
log 2 ( a ) = log 2 ( 1 y ) = − 1 2 log 2 ( y ) \log_{2}(a) = \log_{2}(\frac{1}{\sqrt{y}}) = -\frac{1}{2}\log_{2}(y) log 2 ( a ) = log 2 ( y 1 ) = − 2 1 log 2 ( y )
将浮点数 a a a 和 y y y 转化为二进制形式 A , Y A, Y A , Y ,代入上式,得到
A 2 23 − 127 = − 1 2 ( Y 2 23 − 127 ) \frac{A}{2^{23}}-127 = -\frac{1}{2}\left( \frac{Y}{2^{23}}-127 \right) 2 23 A − 127 = − 2 1 ( 2 23 Y − 127 )
A = 381 × 2 22 + α − 1 2 Y A = 381 \times 2^{22} + α - \frac{1}{2}Y A = 381 × 2 22 + α − 2 1 Y
这个式子,其实就是这行代码的含义:
i = 0x5f3759df - ( i >> 1 ); // what the fuck?
但 0x5f3759df 这个值是怎么来的呢?或句话说,如何计算出最佳的修正因子α α α ?
引入一个修正因子α α α ,使得直线 y = x y=x y = x 上移与 y = log 2 ( 1 + x ) y=\log_{2}(1+x) y = log 2 ( 1 + x ) 在图像上更加接近。
log 2 ( 1 + x ) ≈ x + α \log_{2}(1+x) \approx x+α log 2 ( 1 + x ) ≈ x + α
切比雪夫最佳逼近
E ( α ) = max x ∈ [ 0 , b ] ∣ ( x + α ) − log 2 ( 1 + x ) ∣ E(\alpha) = \max_{x \in [0,b]} \left| (x + \alpha) - \log_2(1 + x) \right| E ( α ) = x ∈ [ 0 , b ] max ∣ ( x + α ) − log 2 ( 1 + x ) ∣
最佳逼近直线为
y = x + 0.0431 y=x+0.0431 y = x + 0.0431
Refs
float32 Picture
(By Fresheneesz at the English Wikipedia project, CC BY-SA 3.0, https://commons.wikimedia.org/w/index.php?curid=3357169 )
NewtonMethod Picture
作者 Ralf Pfeifer - de:Image:NewtonIteration Ani.gif,CC BY-SA 3.0,https://commons.wikimedia.org/w/index.php?curid=2268473