回顾《雷神之锤III》平方根倒数算法
关于平方根倒数快速算法
《雷神之锤III》这款游戏的源码公布后,有人在里面发现了一个好玩的算法,是关于平方根倒数的算法。虽然该算法如今已经过时了,但是其中涉及的浮点数标准和牛顿迭代法还是挺值得一看的。
该算法一般用于求向量模长等地方,可以说在游戏建模中涉及物理还原这块用到这个平方根倒数还挺多的。
如果给我一个公式如下:
$$
f=\frac{1}{\sqrt{x}}
$$
我想我可能会写下
1 |
|
但是在《雷神之锤III》里的算法并不是math.h
里的算法。
源码
1 | float Q_rsqrt( float number ) |
这个Q_rsqrt
函数可以说是越看越怪,其中的0x5f3759df
是哪里来的简直一头雾水。
IEEE754
首先需要了解浮点数的存储,IEEE二进制浮点数算术标准(IEEE 754
),以下为例子,具体请自行了解。
其中第一位0正1负。
第一步:
1 | i = ( long )y; |
将y
强制转换为long
格式是为了让y能够进行位操作,float
并不能进行位操作。 (long 32bit)
但是转换会有误差产生,比如y=3.33在经过强制转换为long格式后就会变为3。同时内存小数部分也被打乱。
1 | 3.33 0 10000000 10101010001111010111000 //IEEE 754 |
此时并不能达到要求,即对float
格式的y进行位操作,毕竟内存中的位都改变了。
于是就有了
1 | i = * ( long * ) &y; // evil floating point bit hack |
这里首先通过&y
获得了y
的地址,然后通过(long *)
将改地址转换为long
格式的地址,最后通过*
获取地址内的值返回给i。
此时
上面这句话就是对第一步的理解,通俗来说就是原来IEEE 754
的y
的地址值是float
格式,但是要保留地址内值不变的情况下告诉CPU
这是long
格式。
第二步:
1 | i = 0x5f3759df - ( i >>1 ); // what the fuck? |
第三步:
牛顿迭代法
$$
f(x)=\frac{1}{x^{2}}-x
$$
$$
f^{\prime}(x)=\left.f^{\prime}(y)\right|_{y=x}=-\frac{2}{y^{3}}=-\frac{2}{x^{3}}
$$
$$
x_{\text {new }}=x-\frac{f(x)}{f^{\prime}(x)}=x-\frac{\frac{1}{x^{2}}-x}{-\frac{2}{x^{3}}}=\frac{3}{2} x-\frac{1}{2} x^{4}
$$