何が起こっているのかについて、よりコンパクトで数学的な説明を次に示します。ましょうと入力されると、既に低減モジュロである、そうと。(コード的には、これは行の後を意味します。)を計算する必要があります。つまり、設定して、一部のに対してとなるようなを見つけます。abma<mb<mb %= m
abmodmx=abrxx=qxm+rxqx
あふれないケース
オーバーフローしないケースでは、モジュラスを計算してそれで完了できます。代わりに、コードは次を計算します:
Nowます。ここで私は懸念を回避しました。が浮動小数点変数の仮数に収まらない可能性があります。ただし、適合する限り、これは問題にはなりません。最終的にここで、は、大きさがより小さいです。前と同じように続行します。
y=⌊xm⌋=⌊qx+rxm⌋=qx+⌊rxm⌋=qx
x−ym=qxm+rx−qxm=rxxmx=ab<m2x′=x+eemy=⌊x′m⌋=⌊qx+rx+em⌋=qx+⌊rx+em⌋
この場合、およびであるので、削除できません。ある場合であってもよいまたは、それは確かに間(厳密に)であろうが、及び、そうはまたはいずれかです。ここで
⌊rx+em⌋rx<me<mrx+e>mrx+e<0−m2m⌊rx+em⌋0±1x−ym=qxm+rx−qxm−⌊rx+em⌋m=rx−⌊rx+em⌋m
ここでモジュラス演算を実行すると、余分な項が取り除かれますが、Cが選択する
規則により
(-1)%m = -1
ます。常に正の数を返す規則を取得するには、負の場合、結果にを追加します。
m
あふれる事件
我々は整数演算MODやっていると仮定しましょう例えば、、とのは、仮定しよう手段。これで乗算がラップされます。整数場合、と記述します。つまり、計算により数値が得られます。浮動小数点の計算は、以前と同様に、が仮数に収まることを前提としています(が大きい場合は、80ビットの拡張精度浮動小数点数が必要になる場合があります)。係数については、および定義します。、N264ab>Nm2>N>mx=ab=z+kNk<ma*b
zmmz=qzm+rzkN=qNm+rNx=qzm+qNm+rz+rN。前と同様に、を浮動小数点変数として格納すると、丸め誤差が発生する可能性がありますなので、ここでもです。以前と同じ計算を行うと、次のようになります。
ここでウサギを帽子から取り出しました。ここでは、加算mod、およびmodため、です。前と同じように、 mod を取得します。x|e|<mx′=x+e
z−⌊x′m⌋m=qzm+rz−qzm−qNm−⌊rz+rN+em⌋m=rz−qNm−⌊rz+rN+em⌋m=rz+rN−⌊rz+rN+em⌋m
NN qNm+rN=kN=0qNm=−rNmrz+rN=xmodm、そして以前のように、Cの慣例はこれが負になる可能性があり、修正する必要があります。
残っている潜在的な問題が1つあります。はます。でない限り、これは問題を引き起こしません。この場合、間違った答えが返されます。以下のためのこの手段は。(余談ですが、場合、になるので問題はありません。)になるように浮動小数点ハードウェアを切り捨てて構成すると、このケースは発生しません。(仮数がを保持できるという私の制限を忘れないでください!)。⌊rz+rN+em⌋22m>NN=264m>2632m=N0e≤0m
これを最上位/最下位ビットに接続する
引用した部分に具体的に対処するために、「base」のように、ベースとして考える2より大きい整数を考えます。通常の場合、はとして表されます。浮動小数点表現では、これをと記述します。ここで、2つの(正の)1 桁の桁の数値を乗算したいとしましょう。結果には最大で2桁が必要になります。たとえば、([標準]の桁の表現で必要な場合)および。1つのベースのみを保存できるように制限されている場合、B10B=102121=2B+12.1×B1BcB+dB0≤c<B0≤d<BB結果の桁。2つの明らかな選択肢、またはます。cd
選択することは、modをとして機能させることと同じです。これは、整数演算で発生するものです。(ちなみに、アセンブリレベルでは、整数の乗算でこれらの両方の数字が生成されることがよくあります。)dBcB+d=dmodB
一方、浮動小数点演算はを選択することに実質的に対応しますが、指数をインクリメントすることで補正します。実際には、結果をとして表しますが、格納できるのは1 桁の桁のみなので、これはちょうどます。(実際には、我々は大きなベースに、小型基地(すなわち2)ではなく、1桁または2桁の数字を複数桁番号などの数字を検討します。これは、私たちは、より高い数字の一部保存することができますの場合を彼らは、店舗には必要ありませんされていないが、すべての最悪のシナリオには失われている。のいずれもcc.d×B1Bc×B1dcdc指数の部屋がなくなるまで失われます。上記のコードの場合、これは問題ではありません。)
を浮動小数点形式で忠実に表現できる限り、式は、base-でその上位桁を抽出したものと見なすことができます。上記のコードと数学は、数値の進数表現と進数表現の間の相互作用として見ることができます。m⌊abm⌋mNm
実用性
このドラフトのセクション5.2.4.2.2に基づくと、C11標準でlong double
は仮数部の長さがおよそ33ビットである必要があるようです。(特に、それが唯一の最小数を指定する表示小数点忠実に表現することができる桁。)実際には、汎用のCPU、特にのx86ファミリーのCPUを対象とする場合、ほとんどのCコンパイラは、IEEE754型を使用します。この場合double
、仮数は53ビットになります。x86ファミリのCPUは、事実上64ビットの仮数で80ビット形式をサポートします。すべてではないがいくつかのコンパイラはlong double
、x86をターゲットとする場合にそれを示します。コードの有効範囲は、これらの実装の詳細によって異なります。
a*b - c*m
int64_t
int64_t
%m