整数の平方根アルゴリズム

sqrt64q, radix-4 digit-recurrence
2026-01-10
Takayuki HOSODA

概要

これは、18〜24-bit の信号処理システム用に設計した、最小誤差丸め の 64-bit 平方根(square root)アルゴリズムと C での実装例です。

ハードウェア化を念頭に、2-bit 単位、すなわち radix-4 での漸近近似を行っています。 全ビットの近似が完了した時点で LSB を調整し、誤差を ±1/2 LSB に収めています。

64-bit の符号なし整数の最小誤差丸め平方根の最大値は 232 になるため、戻り値には 33-bit が必要となります。

アルゴリズム

このアルゴリズムは 基数 4 逐次桁法 (radix-4 digit-recurrence) による平方根で、
xn+1 = 4xn + k,   k ∈ {0, 1, 2, 3}
を選びながら、
(xn+1)2An
を満たす最大の k を選ぶ方式です。

ここで An は radix-4 において、 その時点で評価に用いる被開平数の prefix(上位ビット群)を表します。
コード中では an = a >> (4 * i - 4)An に対応します。

平方の更新は
(4x + k)2 = 16x2 + 8xk + k2
なので、コード中の
(xs << 4) + ((x << 3) + k) * k
は正確にこの式を実装しています。 また比較側は
(xs << 4) + ((x * k) << 3) + (k * k)
を明示的に書き下しており、完全に同一です。 従って、このループは
「上位 2bit ずつ取り込みながら、常に平方が prefix 以下になる最大の桁を選ぶ」
という radix-4 での平方根の定義そのものになっています。

LSB 補正

最後の
if (a - x > xs)
    x++;
は、誤差解析からの処理です。 ループ終了時点では
x2a < (x + 1)2
ではなく、radix-4 digit recurrence による truncation なので
a - x2 ∈ [0, 2x]
の範囲になります。 ここで、
(x + 1)2 = x2 + 2x + 1
なので
a > x2 + x
この条件に相当するコードが
if (a - x > xs)
になっています。 これは、「1/2 LSB 丸め」ではなく、
floor(sqrt(a) + 0.5)
に一致する正しい最近接整数丸めです。

誤差分布

誤差のヒストグラムは、例えば 38-bit システムにおける 0 〜 238 - 1(全 238 個、最大値 274877906943 = 0x3fffffffff) の全数検査では、次のようになる。

error(-1/4..-1/2 LSB) =  68719476736
error(-1/4..+1/4 LSB) = 137438953472
error(+1/4..+1/2 LSB) =  68719476736

総サンプル数 238 個に対して、最近接整数丸めにおける誤差区間幅が 1/4, 1/2, 1/4 LSB であることに対応し、1:2:1 の分布になっていることが確認できる。

C 実装

sqrt64q -- 与えられた 64-bit 符号なし整数の平方根に対して誤差が最小となる整数を返す。

/* sqrt64q - calculate the square root of a given unsigned integer
 * Rev.1.0 (2026-01-12) (c) 2026 Takayuki HOSODA
 * SPDX-License-Identifier: BSD-3-Clause
 */
#include <inttypes.h>
uint64_t sqrt64(uint64_t a);
uint64_t sqrt64(uint64_t a) {
    uint64_t x       = 0;        // current approximation
    uint64_t xs      = 0;        // square of the current approximation
    uint64_t k          ;        // trial digit in radix-4 (0..3)
    uint64_t an         ;        // A_n: current prefix of the radicand
    for (int i = 2 * sizeof(a); i > 0; i--) {
        if ((an = a >> (4 * i - 4))) {
            k    = (((xs << 4) +   x * 24  + 9) <= an) ? 3 :
                   (((xs << 4) +   x * 16  + 4) <= an) ? 2 :
                   (((xs << 4) +   x *  8  + 1) <= an) ? 1 : 0;
            xs   =   (xs << 4) + ((x << 3) + k) * k;
            x    =   (x  << 2) + k;
        }                        // skip if upper digits are zero
    }
    if (a - x > xs)              // residual = a - x^2 is in [0, 2x]
        x++;                     // round to the nearest integer sqrt
    return x;
}

Note:
UINT64_MAX (0xffffffffffffffff) の平方根は、0x100000000 (= 4294967296) となって、 UINT32_MAX (0xffffffff) よりも大きくなるため、戻り値を uint32_t にすることは出来ません。

ハードウェア実装観点での最適化

k の決定ロジック

現在の
k = (… ? 3 : … ? 2 : … ? 1 : 0);
は比較が 3 つ直列ですが、これらは回路では 3 個のマグニチュードコンパレータになります。 これは radix-4 として最小です。 比較式は
16x2 + 8xk + k2A
なので、 になり、非常に合成しやすい形です。

乗算器の扱い

このコードで必要な乗算は だけです。 これらは全て に落ちます。 汎用 multiplier(乗算器)を一切要求しない構造で、ASIC / FPGA 向けに理想的です。

xs の保持

平方を再計算せずに xs を保持しているのは、 このクラスのアルゴリズムでは最も重要な高速化です。

1bit-radix だと減算型が多いですが、 radix-4 で xs を持つ方式は比較器中心の設計になり、 高速化(高周波数化)しやすい構成です。

まとめ

このアルゴリズムは「数値演算」ではなく「比較問題」です。
16x2 + 8xk + k2A
を 3 回比較しているだけで、これは CPU でも極めて計算コストの低い命令列になります。

Newton–Raphson 法は FPU や乗除算器を使用しますが、こちらは整数 ALU と比較器だけで完結します。

ハードウェア化においても、各ステップで必要なのは: しかも 32 step で必ず終了(64-bit 版の場合)。
これは、典型的な CORDIC 型digit recurrence 型 の回路で、 と、実にハードウェア化に適した構造となっています。
✅ 付随的ですが、数値的信頼性も高く、シンプルで移植性も高いものとなっています。

REFERENCES

SEE ALSO


www.finetune.co.jp [Mail] © 2000 Takayuki HOSODA.