快速平方根倒数算法深度理解
快速平方根倒數(shù)算法深度理解
快速平方根倒數(shù)算法是什么?
簡單來說這個算法避開了開方和除法運算快速實現(xiàn)了
y = 1 x y= \frac{1}{\sqrt x} y=x?1?
快速平方根倒數(shù)算法首次出現(xiàn)是在著名的雷神之錘3的圖形計算優(yōu)化代碼中
話不多說先上c代碼
一.代碼
經(jīng)大佬測試代碼比(float)(1/sqrt(x))快4倍。
初步觀看代碼:
①定義了32位整型變量i
②定義了兩個32位浮點數(shù)
③將1.5存入一個常量
④將形參number除2存入x2
⑤將形參number存入y
之后的四行代碼乍一看???莫慌下面開始正文
二.理解算法前的知識鋪墊
1.IEEE 754 (二進制浮點數(shù)算術(shù)標準)
32位分為三個部分:符號位 指數(shù)部分
①符號位:0為正數(shù) 1為負數(shù)
②8位指數(shù)部分:8位可以表示0到255所有的數(shù)字,但我們還需要得到負指數(shù)。
因此我們對0-255減去127就可以得到-127到128之間所有的整數(shù)。
舉個例子如果指數(shù)部分我想要4
那這八位必須表示4+127=131 即10000011
這樣通過移碼我們可以將正負指數(shù)都變成0-255之間的整數(shù)
③23位尾數(shù):利用23位我們可以表示0到2^23-1之間的整數(shù)
對于十進制科學計數(shù)法,尾數(shù)是1到10
對于二進制科學計數(shù)法,尾數(shù)是1-2
11000 = 1.1 ? 2 4 11000=1.1*{2}^{4} 11000=1.1?24
0.0101 = 1.01 ? 2 ? 3 0.0101=1.01*{2}^{-3} 0.0101=1.01?2?3
在科學計數(shù)法中,第一位按照定義不可以是0,對于二進制這個數(shù)只能是1。因此我們不存儲這一位。
說了一堆理論的我們舉一個例子看一下浮點數(shù)是怎么存儲在計算機中的:
我們拿 8.25 舉例子
Ⅰ.寫成二進制:1000.01
Ⅱ.變成科學計數(shù)法: 1.00001 ? 2 3 = ( 1 + 0.00001 ) ? 2 3 1.00001 *{2^3}=(1 + 0.00001) * {2^3} 1.00001?23=(1+0.00001)?23
Ⅲ.解釋
指數(shù)部分我們用 8bit 表示 3+127
3對應(yīng)的就是2^3中的3
(1 + 0.00001):加號前面的1我們丟掉不存
對于0.00001中小數(shù)點后的數(shù)我們用23位尾數(shù)去存
羅嗦了這么久現(xiàn)在我們可以輕松理解以下內(nèi)容:
對于一個浮點數(shù)x(十進制或者二進制)有:
x = ( ? 1 ) s ? ( 1 + m ) ? 2 e x=(-1)^s*(1+m)*{2}^{e} x=(?1)s?(1+m)?2e
對于下式 ( 1 + 0.00001 ) ? 2 3 (1 + 0.00001) * {2^3} (1+0.00001)?23
m : 00001 m:00001 m:00001
e : 3 e:3 e:3
我們將存取之后的8位指數(shù)部分用E表示
將存取之后的23位尾數(shù)部分用M表示
E : 3 + 127 E:3+127 E:3+127
M : 00001 ? ( 補 18 個 0 填 滿 23 位 ) M:00001\cdots(補18個0填滿23位) M:00001?(補18個0填滿23位)
我們可以得出E e和M m的關(guān)系式
E = e + 127 E=e+127 E=e+127
M = 2 23 ? m M={2}^{23}*m M=223?m
三.數(shù)學推導
y = 1 x y= \frac{1}{\sqrt x} \quad y=x?1?
取對數(shù):
① log ? 2 y = ? 0.5 ? log ? 2 ①\log_2 y=-0.5* \log_2 ①log2?y=?0.5?log2?
將y和x分別用二進制科學計數(shù)法表示:
② y = ( 1 + m y ) ? 2 e y ②y=(1+m_y)*{2}^{e_y} ②y=(1+my?)?2ey?
③ x = ( 1 + m x ) ? 2 e x ③x=(1+m_x)*{2}^{e_x} ③x=(1+mx?)?2ex?
聯(lián)合上述三個式子化簡:
l o g 2 ( 1 + m y ) + e y = ? 0.5 ? ( l o g 2 ( 1 + m x ) + e x ) log_2 (1+m_y)+e_y=-0.5*(log_2(1+m_x)+e_x) log2?(1+my?)+ey?=?0.5?(log2?(1+mx?)+ex?)
通過一次線性近似:
m y + b + e y = ? 0.5 ? ( m x + b + e x ) m_y+b+e_y=-0.5*(m_x+b+e_x) my?+b+ey?=?0.5?(mx?+b+ex?)
m y = M y 2 23 m_y= \frac{M_y}{{2}^{23}} \quad my?=223My??
m x = M x 2 23 m_x= \frac{M_x}{{2}^{23}} \quad mx?=223Mx??
E y = e y + 127 E_y=e_y+127 Ey?=ey?+127
E x = e x + 127 E_x=e_x+127 Ex?=ex?+127
上述五個式化簡:
M y + 2 23 ? E y = 1.5 ? 2 23 ? ( 127 ? b ) ? 0.5 ? ( M x + 2 23 ? E x ) M_y+{2}^{23}*E_y=1.5*{2}^{23}*(127-b)-0.5*(M_x+{2}^{23}*E_x) My?+223?Ey?=1.5?223?(127?b)?0.5?(Mx?+223?Ex?)
令
I y = M y + 2 23 ? E y I_y=M_y+{2}^{23}*E_y Iy?=My?+223?Ey?
I x = M x + 2 23 ? E x I_x=M_x+{2}^{23}*E_x Ix?=Mx?+223?Ex?
對于 1.5 ? 2 23 ? ( 127 ? b ) 1.5*{2}^{23}*(127-b) 1.5?223?(127?b)我們令其為K,可以知道通過調(diào)節(jié)b的大小就可以改變K的值
前輩們給出了當
b = 0.0450465 b=0.0450465 b=0.0450465時有K=
K = 0 x 5 f 3759 d f K=0x5f3759df K=0x5f3759df
這剛好對應(yīng)代碼中:
通過一系列不怎么復雜的數(shù)學推導我們究竟得到了什么?
我們知道了一個輸出結(jié)果的整形和輸入整形之間的關(guān)系,即:
I y = K ? 0.5 ? I x I_y=K-0.5*I_x Iy?=K?0.5?Ix?
四.代碼解釋
①
我們將數(shù)字number存入y中準備對其進行位操作,但是眾所周知浮點數(shù)沒有位操作的語句。
但是長整型數(shù)可以進行位操作!
如果執(zhí)行下面這一行代碼:
i=long(y); y=4.456時i=4但是我們想要的是將y逐位轉(zhuǎn)化成長整型,換句話說按照上面的代碼我們改變了二進制逐位的值。
因此我們采取下面代碼:
這句話干了什么呢?
簡單來說它轉(zhuǎn)換了y對應(yīng)的內(nèi)存地址而不是y這個數(shù)字本身。
或者說這句話告訴cpu把y指向的內(nèi)存數(shù)據(jù)當成長整形來讀取
6666指針確實是個神奇的東西,我們繼續(xù)向下
通過這句話浮點數(shù)y的二進制表示存入到了i中。
②
這句的理解在第三大部分可以輕松找到。
簡單來說我們通過放縮系數(shù)和平移操作代替了除法和開方的過程,這大大提高了代碼運行效率。
③
和①類似我們再次通過這種操作將二進制位轉(zhuǎn)換成浮點數(shù)。
通過上述三步我們得到了一個近似值。因此我們還需要進行牛頓迭代法繼續(xù)使結(jié)果精確。
④
下面進行牛頓迭代法的數(shù)學推導:
y = 1 x y= \frac{1}{\sqrt x} y=x?1?
f ( y ) = 1 y 2 ? x f(y)=\frac{1}{y^2}-x f(y)=y21??x
y n + 1 = y n ? 1 y n 2 ? x ? 2 y n 3 = y n ? ( 3 2 ? 1 2 ? x ? y n 2 ) y_{n+1}=y_n-\frac{\frac{1}{y_n^2}-x}{\frac{-2}{y_n^3}}=y_n*(\frac{3}{2}-\frac{1}{2}*x*y_n^2) yn+1?=yn??yn3??2?yn2?1??x?=yn??(23??21??x?yn2?)
五.總結(jié)
短短的幾行代碼背后是強大的數(shù)學支撐,萬萬想不到之前學過的高數(shù)會在算法中這樣體現(xiàn)。
第一次試寫涉及到數(shù)學公式推導的文章,如有疑問請大家多多留言!
總結(jié)
以上是生活随笔為你收集整理的快速平方根倒数算法深度理解的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Kafka 认证三:添加 Kerbero
- 下一篇: 尽可能帮助他人