以下にあるように、 除算を扱うために相当な数のワードがあることが分かります。 これらの主な違いは、 符号付き除算の処理にあります。
これらのワードはどのようにして符号付き除算に対応するのでしょうか? (U
プレフィックスが付いたワードでは符号付き除算に対応しません)
商を四捨五入して整数に丸める場合、 負の無限大に向かって丸めるのでしょうか(floored division(床除算)、 接尾辞 F
)、
それとも 0 に向かって丸めるのでしょうか(symmetric division、接尾辞 S
)。 標準では、
ほとんどの標準ワード(/ mod /mod */ */mod m*/
)について、 問題なのは、 各々の実装依存のままであり、
システムごとに異なる選択が行われています。 Gforth では、これらのワードをfloorとして実装します(Gforth 0.7 以降)。
floored division と symmetric division の違いは、 割られる数と割る数の符号が異なり、 かつ、
割られる数が割る数の倍数で無い場合のみです。 以下の表に組み合わせの結果を示します:
floored symmetric 割られる 割る数 余り 商 余り 商 10 7 3 1 3 1 -10 7 4 -2 -3 -1 10 -7 -4 -2 3 -1 -10 -7 -3 1 -3 1
floored と symmetric とが違いを生む一般的なケースは、 様々な符号の被除数(割られる数) n1 が、 同一の正の除数(割る数) n2 で除算される場合です。 同一の正の除数(割る数) n2 で除算される場合、 通常は floored division が必要で、なぜなら、 同一の正の除数(割る数) n2 で除算される場合、 余りは常に正となり、 被除数(割られる数)に応じて符号が変化しないからです。 また、 floored division では、 n1 が n2 増加すると商は常に 1 増加しますが、 symmetric division では、 -n2<n1<n2 の場合は商は増加しません(この範囲では商は 0 です)。
いずれの場合でも、 floored と symmetric とで違いが生ずる数値を除算する場合は、 どのバリエーションが適切であるかを考えてから、
適切な接尾辞を付けた Gforth ワードか、 標準ワードの fm/mod
または sm/rem
のいずれかを使用する必要があります。
1倍長セル同士の除算:
/
( n1 n2 – n ) core “slash”
n=n1/n2
/s
( n1 n2 – n ) gforth-1.0 “slash-s”
/f
( n1 n2 – n ) gforth-1.0 “slash-f”
u/
( u1 u2 – u ) gforth-1.0 “u-slash”
mod
( n1 n2 – n ) core “mod”
n は n1/n2 の余り(modulus)
mods
( n1 n2 – n ) gforth-1.0 “mod-s”
modf
( n1 n2 – n ) gforth-1.0 “modf”
umod
( u1 u2 – u ) gforth-1.0 “umod”
/mod
( n1 n2 – n3 n4 ) core “slash-mod”
n1/n2 を行い、 n3 が余り(modulus)、 n4 が商です(n1=n2*n4+n3)。
/mods
( n1 n2 – n3 n4 ) gforth-1.0 “slash-mod-s”
n3 が余り(remainder)、 n4 が商
/modf
( n1 n2 – n3 n4 ) gforth-1.0 “slash-mod-f”
n3 が余り(modulus)、 n4 が商
u/mod
( u1 u2 – u3 u4 ) gforth-1.0 “u-slash-mod”
u3 が余り(modulus)、 u4 が商
2倍長セルを1倍長セルで割って1倍長セルの結果を得る; これらのワードは、 一部のアーキテクチャー(AMD64 など)では上記ワードとほぼ同じ速度ですが、 他のアーキテクチャ(さまざまな Aarch64 CPU など)でははるかに遅くなります。
fm/mod
( d1 n1 – n2 n3 ) core “f-m-slash-mod”
Floored division: d1 = n3*n1+n2, n1>n2>=0 or 0>=n2>n1.
sm/rem
( d1 n1 – n2 n3 ) core “s-m-slash-rem”
Symmetric division: d1 = n3*n1+n2, sign(n2)=sign(d1) or 0.
um/mod
( ud u1 – u2 u3 ) core “u-m-slash-mod”
ud=u3*u1+u2, 0<=u2<u1
du/mod
( d u – n u1 ) gforth-1.0 “du-slash-mod”
d=n*u+u1, 0<=u1<u; PolyForth スタイルの混合除算
*/
( ( n1 n2 n3 – n4 ) core “star-slash”
n4=(n1*n2)/n3 中間結果は2倍長
*/s
( n1 n2 n3 – n4 ) gforth-1.0 “star-slash-s”
n4=(n1*n2)/n3 中間結果は2倍長
*/f
( n1 n2 n3 – n4 ) gforth-1.0 “star-slash-f”
n4=(n1*n2)/n3 中間結果は2倍長
u*/
( u1 u2 u3 – u4 ) gforth-1.0 “u-star-slash”
u4=(u1*u2)/u3 中間結果は2倍長
*/mod
( n1 n2 n3 – n4 n5 ) core “star-slash-mod”
n1*n2=n3*n5+n4 中間結果(n1*n2)は2倍長、 n4 は剰余、 n5 は商。
*/mods
( n1 n2 n3 – n4 n5 ) gforth-1.0 “star-slash-mod-s”
n1*n2=n3*n5+n4, 中間結果(n1*n2)は2倍長、 n4 は余り(remainder)、 n5 は商
*/modf
( n1 n2 n3 – n4 n5 ) gforth-1.0 “star-slash-mod-f”
n1*n2=n3*n5+n4 中間結果(n1*n2)は2倍長。 n4 余り(modulus)、n5 は商
u*/mod
( u1 u2 u3 – u4 u5 ) gforth-1.0 “u-star-slash-mod”
u1*u2=u3*u5+u4 中間結果(u1*u2)は2倍長。
除算結果を2倍長セル(double-cell)で得ます。 以下のワード群は上記のワード群よりもはるかに遅いです。
ud/mod
( ud1 u2 – urem udquot ) gforth-0.2 “ud/mod”
符号無し2倍長 ud1 を u2 で割る。 結果は、符号なし2倍長の商 udquot と、 1倍長の余り(remainder) urem です。
m*/
( d1 n2 u3 – dquot ) double “m-star-slash”
dquot=(d1*n2)/u3, 中間結果は3倍長です。 ANS Forth では u3 は正の符号付き数値のみです。
環境クエリ(environmental query) floored
を使用すると、 / mod /mod */ */mod
m*/
が floored division か symmetric division かを確認できます(see Environmental Queries)。
整数除算ワードのもう 1 つの側面は、 整数除算ワードのほとんどがオーバーフローする可能性があり、 かつ、
ゼロによる除算が数学的に定義されていないことです。 これらの条件のいずれかに該当した場合に何が起こるかは、
エンジンやハードウェアやオペレーティング・システムによって異なります。 gforth
エンジンは、適切なエラーである -10
(Division by zero;ゼロ除算) または -11 (Result out of range;範囲外の結果) を throw
しようと懸命に試みます、 が、 しかし、 一部のプラットフォームでは -55(浮動小数点未確認エラー;Floating-point
unidentified fault)が throw されます。 gforth-fast
エンジンでは、 不適切な throw
コード(およびエラー・メッセージ)を生成する場合や、 エラーを生成せずに嘘の値のみを生成する場合があります。 つまり、 あなたは、 そのような条件が
throw されることに賭けるべきではありません。 そして、 あなたが迅速なデバッグを行うためには、 gforth
エンジンは
gforth-fast
エンジンよりも多くのエラーをキャッチし、 より正確なエラーを生成します。