web-dev-qa-db-ja.com

64ビット整数を2つの32ビット整数でシミュレートするセマンティクス(hi、lo)

私は32ビット整数のみをサポートするシステムで作業しています。64ビット(符号なし)整数を使用するために、2つの32ビット整数を使用することにしました。1つは上位32ビット(hi)、もう1つは下位32ビット( lo)。

私もこの[hi、lo]構造の算術を作成する必要がありましたが、そのような構造の既存の算術を検索することはほぼ不可能であることがわかりました。私が見つけることができる唯一の有用なものは、openSUSEで記述された演算です: https://github.com/openSUSE/checkmedia/blob/master/u64.h

私がやろうとしていることの名前はありますか? ([hi、lo]構造で倍幅整数をシミュレートし、ビットごとの演算を使用して算術を実装する)

「任意精度の計算」はcloseですが、私は無限の精度で作業していません。

2
AlphaDelta

標準の数学演算をサポートするシステムが与えられた場合-加算、減算、乗算、除算-onnビット、最初にいくつかのルールを覚えておく必要があります。符号の大きさの表現を使用している場合は、潜在的な値が少し失われますが、計算はより簡単です。符号なしのみを処理している場合は、さらに簡単になります。以下のルールは、2の補数の数値表現を使用していることを前提としています。

  1. 保存できる最大絶対値は、任意のnビットで2 ^(n-1)ですロケーション。合計範囲は-2 ^(n-1)... 0 ... +(2 ^(n-1)-1)です。たとえば、 -32768 ... 0 ... + 32767(16ビットの2の補数)。
  2. 同じ符号を持つ2つの数値の加算は、n + 1結果を表すために必要なビット:2 ^(n-1)+ 2 ^(n-1)== 2 ^ (n)
  3. 反対符号の2つの数値の減算は、n + 1結果を表すために必要なビット。
  4. 2つの数値の乗算は、2n-2結果を表すために必要なビット:2^(n-1) * 2^(n-1) == 2^(n-1+n-1) == 2^(2n-2)
  5. Divisionは常に最も難しいです!どういうわけかゼロ除数を処理する必要があり、整数除算と浮動小数点について丸めるかどうかを考慮する必要がありますか?まあ、浮動小数点標準の実装 IEEE 754 -は簡単ではありませんが、私はそれを実行しました。 Intelが最初のPentiumの精度を台無しにした 浮動小数点の実装 小さなバグがあった。

すべての最新のプロセッサは、加算/減算をサポートしています。浮動小数点ユニットは依然として標準ではなく例外ですが、より広く使用されるようになっています。

説明するシステムでは、2つの32ビット値があり、さらに4つの16ビット値または8つの8ビット値に分割できます。アルゴリズムは同じですが、さらに反復する必要があります。 このページ は、2の補数演算の基本的な規則を説明しています。

キャリー、オーバーフローなどのステータスビットを取得できるように、レジスタレベルのプロセッサアクセスにアクセスできると思います。

X = abとY = cdとResult = KLの2つの64ビット数値が与えられます。ここで、a、b、c、d、K、およびLは数値ランドの「32ビットの数字」であり、次のアプローチが機能します。

添加:

  • L = b + d。加算後、Clをキャリー値に設定します。
  • K = a + c + Cl。 (2桁以上の場合は、桁上げを追跡し、チェーンを上位の桁に移動します。)
  • オプション rule を使用して、KLのオーバーフローをチェックします。

減算:

  • L = b-d。追加後、Clをアンダーフロー(多くのプロセッサーで実行されるのと同じ)値に設定します。
  • K = a-c-Cl。 (3桁以上の場合は、アンダーフロー/キャリーを追跡し、チェーンを上に移動します。)
  • 減算 rule を使用して、KLのオーバーフローをチェックします。

上記のリンクで説明したように、乗算と除算は加算と減算で実行できますが、128ビットの結果を考慮する必要があります。

 subterm bits:    64       96           96         128
=========================================================
X * Y = ab * cd = b*d + b*c << 32 + a*d << 32 + a*c << 64

つまり、4つの乗算と3つの加算です。

...ただし、64ビットの数値ランドよりも大きくなる可能性がある中間条件を考慮する必要があります。

結果を64ビットの数値のうちの2つとして、2つの配列として、または4つの32ビット値を持つ構造体として表します。以下は、配列を大きくすることで任意の精度に拡張できるため、配列の使用法を示しています。

result = R[2]、Rの各エントリは64ビットの数値の1つです。

結果の初期化(アルゴリズムを少し簡略化します。)

R[0].lo = 0;    // call this L, lowest-order result "digit"
R[0].hi = 0;    // call this K
R[1].lo = 0;    // call this J
R[1].hi = 0;    // call this I, highest-order result "digit"

X.hiがa、X.loがb、Y.hiがc、Y.loがdの場合、これは次のようになります。

(KL)  = X.lo * Y.lo;      // 62-bits max, rule #4.
                          // Note no carry possible from K.
                          // Carry from L handled by Addition, above.
(JK) += X.lo * Y.hi;      // 63-bits max, rule #4, #2.
                          // Note that K contains the value from the
                          // previous step, hence the "+=" operator.
                          // Also note no carry possible from J.
                          // Carry from K handled by Addition, above.
(JK) += X.hi * Y.lo;      // 64-bits max, rule #4, #2.
                          // Note that JK contains the value from the
                          // previous step, hence the "+=" operator.
                          // Also note no carry possible from J.
                          // Carry from K handled by Addition, above.
(IJ) += X.hi * Y.hi;      // 62-bits max, rule #4, #2.
                          // Note that J contains the value from the
                          // previous step, hence the "+=" operator.
                          // Also note no carry possible from I
                          // because we kept a 128-bit result.
                          //  If we only wanted a 64-bit result,
                          // the overflow tests would be by testing K.

上記のリンクから追加 rules を適用して、64ビットがオーバーフローしたかどうかを確認します。

減算の場合も、Rcのアンダーフローを追加するのではなく減算して、追跡します。次に、リンクから減算 rules を適用して、64ビットがオーバーフローしたかどうかを判断します。

任意のビット幅の2つの「桁」の加算/減算を多精度で処理した後、それを3、4などに拡張することは非常に簡単です。

次に、簡単にするために、上記の乗算/除算アプローチ link を使用して、2桁のシステムまたはより高精度のシステムで乗算および除算します。

3