筆算の開平法の数値を、10進数から2進数に置き換えただけです。二進数の開平法では、乗除算が不要です。
現代では、なかなかお目にかかれないであろう非力なプロセッサ(演算命令は整数のみで除算がないとか…)で平方根を計算する場合、二進数の開平法が選択肢の1つになります。
サンプル プログラムは整数部で計算をやめていますが、少し弄れば小数部も求められます。
sample.cpp
#include <iostream>
using namespace std;
typedef int32_t I32;
typedef uint32_t U32;
typedef uint64_t U64;
struct sqrt_res
{
U32 root; // 平方根
U32 rem; // 余り
};
// 開平法による符号なし整数(32bit)の平方根
sqrt_res sqrt(U32 x)
{
sqrt_res res;
int shift = 32/2; // 残り回数
U32 root = 0; // 平方根
U32 sub = 0; // 副算用
U32 rem = 0; // 余り
// 上位の "0" をスキップして詰める
if ((x >> 0x10) == 0) { x <<= 0x10; shift -= 16/2; }
if ((x >> 0x18) == 0) { x <<= 0x08; shift -= 8/2; }
if ((x >> 0x1c) == 0) { x <<= 0x04; shift -= 4/2; }
if ((x >> 0x1e) == 0) { x <<= 0x02; shift -= 2/2; }
// 開平法で1ビットずつ求める
do
{
// x から2ビット取り出す
rem = (rem << 2) | (x >> (32 - 2));
x <<= 2;
// "0" の場合で初期化
root <<= 1;
sub <<= 1;
// 判定処理
I32 chk = rem - sub - 1;
if (chk >= 0)
{
// "1" の場合で更新
root += 1;
sub += 2;
rem = chk;
}
}
while (--shift > 0);
res.root = root;
res.rem = rem;
return res;
}
// メイン関数
int main(int argc, char **argv)
{
if (argc != 2)
{
cout << "Usage: " << argv[0] << " N" << endl;
return 1;
}
U64 x = strtoul(argv[1], nullptr, 10);
if (errno == ERANGE)
{
cout << "error: range: " << argv[1] << endl;
return 2;
}
if (x >= (1ULL<<32))
{
cout << "error: range: " << argv[1]
<< " >= " << (1ULL<<32)
<< endl;
return 2;
}
sqrt_res res = sqrt(x);
U32 y = res.root * res.root + res.rem;
if (x != y)
cout << "Bug:" << x
<< " != " << y
<< endl;
cout << "N = " << x
<< " = " << res.root
<< " * " << res.root
<< " + " << res.rem
<< endl;
return 0;
}
実行結果
$ ./sample 127806
N = 127806 = 357 * 357 + 357
例 127806 を2進数の筆算(開平法)で平方根を求めると
127806 : 1_1111_0011_0011_1110
357 : 1_0110_0101
1 0 1 1 0 0 1 0 1
----------------------------
) 01 11 11 00 11 00 11 11 10 1
1 1
----- ---
11 100
0 0
----- ----
11 11 1001
10 01 1
-------- -----
1 10 00 10101
1 01 01 1
---------- ------
11 11 101100
0 0
-------- -------
11 11 00 1011000
0 0
----------- --------
11 11 00 11 10110001
10 11 00 01 1
-------------- ---------
1 00 00 10 11 101100100
0 0
---------------- ----------
1 00 00 10 11 10 1011001001
10 11 00 10 01 1
---------------- ----------
1 01 10 01 01 1011001010