平方根運算的軟體與硬體的加速計算

2021-08-08 20:32:59 字數 3959 閱讀 8774

1.1 二分法

利用二分進行開平方的思想很簡單:假定中值為最終解。假定下限為0,上限為x,然後求中值;然後比較中值的平方和x的大小,並根據大小修改下限或者上限;重新計算中值,開始新的迴圈,直到前後兩次中值的距離小於給定的精度為止。需要注意的一點是,如果x小於1,我們需要將上限置為1。**如下:

float sqrtbybisection(float n)

while(fabsf(mid-last) > eps); //eps為精度,一般為0.000001

return mid;

}

這裡有一點需要特別注意:在精度判別時不能利用上下限而要利用前後兩次mid值,否則可能會陷入死迴圈!這是因為由於精度問題,在迴圈過程中可能會產生mid值和up或low中的乙個相同。這種情況下,後面的計算都不會再改變mid值,因而在達不到精度內時就陷入死迴圈。

1.2 牛頓迭代法

將中值替換為切線方程的零根作為最終解,原理可以利用下**釋:

while(fabsf(val-last) > eps); //eps為精度,一般為0.000001

return

val;

}注意到**中初始值的選擇,解釋如下:

ieee浮點標準用的形式來表示乙個數,將該數存入float型別之後變為:

現在需要對這個浮點數進行開方,我們看看各部分都會大致發生什麼變化。指數e肯定會除以2,127保持不變,m需要進行開方。由於指數部分是浮點數的大頭,所以對指數的修改最容易使初始值接近精確值。幸運的是,對指數的開平方我們只需要除以2即可,也即右移一位。但是由於e+127可能是奇數,右移一位會修改指數,我們將先將指數的最低位清零,這就是& 0xff7fffff的目的。然後將該轉換後的整數右移一位,也即將指數除以2,同時尾數也除以2(其實只是尾數的小數部分除以2)。由於右移也會將127除以2,所以我們還需要補償乙個64,這就是最後還需要加乙個(64<<23)的原因。

這裡大家可能會有疑問,最後為什麼加(64<<23)而不是(63<<23),還有能不能不將指數最後一位清零?答案是都可以,但是速度都沒有我上面寫的快。這說明我上面的估計更接近精確值。下面簡單分析一下原因。首先假設e為偶數,不妨設e=2n,開方之後e則應該變為n,127保持不變,我們看看上述**會變為啥。e+127是奇數,會清零,這等價於e+126,右移一位變為n+63,加上補償的64,指數為n+127,正是所需!再假設e為奇數,不妨設e=2n+1,開方之後e應該變為n+1(不精確),127保持不變,我們看看上述**會變為啥。e+127是偶數等於2n+128,右移一位變為n+64,加上補償的64,指數為n+1+127,也是所需!這確實說明上述的估計比其他方法更精確一些,因而速度也更快一些。

1.3 卡馬克演算法—快速平方根倒數演算法

此法實質上是進行了1~2次的牛頓迭代法求開方倒數,求平方根的倒數,實際就是求方程1/(x^2)-a=0的解。將該方程按牛頓迭代法的公式展開為:x[

n+1]

=1/2*x [ n ] *(3-a*x[n]*x[n])$。這個方法的乙個關鍵地方還在於起始值的選取,演算法選取乙個「magic number」,使演算法的初始解非常接近精確解!

float sqrtbycarmack( float number )  

2.1 手工開平方演算法法先以10進製為例,解釋手動開平方演算法:

首先將數字每兩位分成一段。如:745836942。就分成:

7|45|83|69|42,即從個位開始分。共分成五段,第一段是7。

對第一段的數字7開方取整,可得a=2。此時,要在2後面接乙個數字b,並在7後面加上下一段的數45,使產生的兩位數ab的平方不大於745。

我們知道,數ab實際值表示為10a+b,其平方是

100a2+

20ab+

b2。我們可以暫時忽略b2

,而產生乙個「試商」b。即b=

(745

−100a2

)/20a

=(745−

100∗2∗

2)/(

20∗2)

=8.625≈8

(向下取整)。但是,我們發現282

=784

>

745 ,於是這個試商需要減少為7。然而,當a=0時,上述求試商的方法不在適用,但我們可以直接取下一段的兩位數開方。如√45≈6。求出試商後,用

745−ab

2 得到新的「第一段」的數。

具體過程:

取出第一段的數mn,開方得到a,然後接上第二段的數pq,用mn

pq−100a2

得到「餘數」x,x/

20a得到試商b,然後調整b(當20∗

a∗b+

b∗b>

x 時b需要減少),調整後,將x−

20a∗b

−b∗b

作為新的餘數x』,ab就是第二輪開平方的結果。由於前面已經將

100a

2 減掉,所以後面每次都不用再減去

100a

2 。重複步驟,直到開方完畢,或達到要求的精度為止。最後得到的a就是平方根。

如求745836942

的平方根: 7|

45|83|

69|42 ①a

=sqr

t(7)

≈2,b

=(745−

400)/40

≈8,28

2=784>

745==

>b=

7==>272

=729

<

745,

745−

729=

16 ②a

=27,b

=1683

/540≈3

,27∗20

∗3+3

∗3=1629

<

1683

,1683

−1629=54

③a=273,b=

5469

/5460≈1

,273∗20

∗1+1

∗1=5461

<

5469

==>b=

1,5469

−5461=8

當運用到二進位制時,數ab實際值表示為a<<1+b,其平方是a2

<<2+

ab<<2+

b2。我們可以暫時忽略b2

,而產生乙個「試商」b,依此類推下去。以下**為求乙個32位數的平方根,演算法將原始值兩兩分組進行計算,注意:根應至多為16位且每次計算的b值只能是0或1:

unsigned

int sqrt_16_1(unsigned

int m)

} return n;

}

將原始值分類大小由2變成4,那麼每次要計算2位二進位制數值,可能情況有00,01,10,11,00情況時由於不更新結果,直接移動兩位即可,所以可不進行處理,內部迴圈因此為1~3!

也可把分類大小變成8,16…只要是2的倍數均可,演算法流程類似!

具體**如下:

unsigned

int sqrt_16_2(unsigned

int m)

m <<= 4;

for (j=3;j>0;j--)}}

return n;

}//32->16

計蒜客 X的平方根

設計函式int sqrt int x 計算 的平方根。輸入乙個整數 輸出它的平方根。直到碰到檔案結束符 eof 為止。對於每組輸入,輸出一行乙個整數,表示輸入整數的平方根。樣例輸入 123 4567 89樣例輸出 111 2222 23python 一 用try except try while t...

Python 計蒜客 X的平方根

設計函式int sqrt int x 計算 x 的平方根。輸入格式 輸入乙個 整數 xx,輸出它的平方根。直到碰到檔案結束符 eof 為止。輸出格式 對於每組輸入,輸出一行乙個整數,表示輸入整數的平方根。樣例輸入 1 2 3 4 5 6 7 8 9 樣例輸出 1 1 1 2 2 2 2 2 一 用t...

LintCode x的平方根

x的平方根 實現 int sqrt int n 函式,計算並返回 n 的平方根。樣例 sqrt 3 1 sqrt 4 2 sqrt 5 2 sqrt 10 3 挑戰 o log x solution 如果使用蠻力法來求解的話,肯定是會超時的。因此,我們需要使用牛頓迭代法來求解這問題 牛頓迭代法 對於...