我們先來看看10進制下是如何手工計算開方的。
先看下面兩個算式,
我們把公式(2)改寫爲如下格式:
這個算式左右都有q,因此無法直接計算出q來,因此手工的開方算法和手工除法算法一樣有一步需要猜值。
我們來一個手工計算的例子:計算1234567890的開方
首先我們把這個數兩位兩位一組分開,計算出最高位爲3。也就是(3)中的p,最下面一行的334爲餘數,也就是公式(3)中的(x^2 - 100*p^2)近似值
3
---------------
| 12 34 56 78 90
9
---------------
| 3 34
下面我們要找到一個0-9的數q使它最接近滿足公式(3)。我們先把p乘以20寫在334左邊:
3 q
---------------
| 12 34 56 78 90
9
---------------
6q| 3 34
我們看到q爲5時(60+q*q)的值最接近334,而且不超過334。於是我們得到:
3 5
---------------
| 12 34 56 78 90
9
---------------
65| 3 34
| 3 25
---------------
9 56
接下來就是重複上面的步驟了,這裏就不再囉嗦了。
這個手工算法其實和10進制關係不大,因此我們可以很容易的把它改爲二進制,改爲二進制之後,公式(3)就變成了:
我們來看一個例子,計算100(二進制1100100)的開方:
1 0 1 0
---------------
| 1 10 01 00
1
---------------
100| 0 10
| 0 00
---------------
| 10 01
1001| 10 01
---------------
0 00
這裏每一步不再是把p乘以20了,而是把p乘以4,也就是把p右移兩位,而由於q的值只能爲0或者1,所以我們只需要判斷餘數(x^2 - 4*p^2)和(4*p+1)的大小關係,如果餘數大於等於(4*p+q)那麼該上一個1,否則該上一個0。
下面給出完成的C語言程序,其中root表示p,rem表示每步計算之後的餘數,divisor表示(4*p+1),通過a>>30取a的最高 2位,通過a<<=2將計算後的最高2位剔除。其中root的兩次<<1相當於4*p。程序完全是按照手工計算改寫的,應該不難理解。
unsigned long rem = 0;
unsigned long root = 0;
unsigned long divisor = 0;
for(int i=0; i<16; i++){
root <<= 1;
rem = ((rem << 2) + (a >> 30));
a <<= 2;
divisor = (root<<1) + 1;
if(divisor <= rem){
rem -= divisor;
root++;
}
}
return (unsigned short)(root);
}