10進浮動小数点(DFP )演算とは、 10進浮動小数点数の表現と演算の両方を指します。10進数(基数10)の分数を直接扱うことで、通常、10進数(測定値や財務情報など、人間が入力するデータでよく見られる)と2進数(基数2)の分数の間で変換する際に発生する丸め誤差を回避できます。
10進浮動小数点表現は、10進固定小数点表現や整数表現に比べて、はるかに広い範囲の値をサポートできるという利点があります。たとえば、10進数8桁と小数点以下2桁を割り当てる固定小数点表現では、123456.78、8765.43、123.00などの数値を表現できますが、10進数8桁の浮動小数点表現では、1.2345678、1234567.8、0.000012345678、12345678000000000などの数値も表現できます。この広い範囲により、連続計算中の丸め誤差の蓄積を大幅に遅らせることができます。たとえば、Kahan加算アルゴリズムは、丸め誤差の漸近的な蓄積なしに多数の数値を加算するために浮動小数点数で使用できます。
初期の機械式計算機における10進浮動小数点数の使用例は、そろばん、計算尺、スモールウッド計算機、および科学的記数法での入力をサポートするその他の計算機に見られる。機械式計算機の場合、指数は多くの場合、別途考慮される補助情報として扱われる。
IBM 650コンピュータは 1953 年に 8 桁の 10 進浮動小数点形式をサポートしました。[ 1 ] [ 2 ]それ以外はバイナリであるWang VSマシンは 1977 年に 64 ビットの 10 進浮動小数点形式をサポートしました。[ 3 ] Motorola 68881 は1984 年に仮数部 17 桁、指数部 3 桁の形式をサポートし、Motorola 68040プロセッサ用の浮動小数点サポート ライブラリは 1990 年に互換性のある 96 ビットの 10 進浮動小数点ストレージ 形式を提供しました。[ 3 ]
PL/I、.NET、[ 4 ] calc を使用したEmacs、Pythonの decimal モジュールなど、一部のコンピュータ言語には、10 進浮動小数点演算の実装があります。 [ 5 ] 1987 年にIEEE は、10 進浮動小数点を使用した計算の標準であるIEEE 854をリリースしましたが、他のシステムとの交換のために浮動小数点データをどのようにエンコードするかについての仕様がありませんでした。これはその後、2 つの異なる代替方法を使用して 10 進浮動小数点データのエンコードを標準化したIEEE 754-2008で対処されました。
IBM POWER6およびそれ以降の POWER プロセッサは、IBM System z9 [ 6 ] (およびそれ以降の zSeries マシン) と同様に、ハードウェアで DFP を内蔵しています。SilMinds は、構成可能なベクトル DFPコプロセッサである SilAx を提供しています。[ 7 ] IEEE 754-2008 では、これをより詳細に定義しています。富士通も、ハードウェアで DFP を内蔵した 64 ビットSPARCプロセッサを提供しています。[ 8 ] [ 3 ]
IEEE 754-2008規格では、32 ビット、64 ビット、128 ビットの 10 進浮動小数点表現が定義されています。2 進浮動小数点形式と同様に、数値は符号、指数、仮数に分けられます。2 進浮動小数点とは異なり、数値は必ずしも正規化されているわけではありません。有効桁数が少ない値には、1×10 2 =0.1×10 3 =0.01×10 4など、複数の表現方法があります。仮数がゼロの場合、指数は任意の値をとることができます。
指数範囲は、正規化された値の範囲がほぼ対称になるように選択されました。可能な指数値の数が偶数の場合、これを正確に実現することはできないため、Emaxに余分な値が割り当てられました。
2つの異なる表現方法が定義されています。
どちらの選択肢も、表現可能な値の範囲は全く同じである。
指数部の最上位 2 ビットは 0~2 の範囲に制限され、仮数部の最上位 4 ビットは 0~9 の範囲に制限されます。可能な 30 通りの組み合わせは 5 ビットのフィールドにエンコードされ、無限大とNaNを表す特別な形式も用意されています。
仮数の上位4ビットが0~7の場合、エンコードされた値は次のように始まります。
s 00mmm xxx 指数部は00で始まり、仮数部は0mmmで始まる s 01mmm xxx 指数部は01で始まり、仮数部は0mmmで始まる s 10mmm xxx 指数は10から始まり、仮数は0mmmで始まる
仮数の先頭4ビットが2進数で1000または1001(10進数で8または9)の場合、数値は次のように始まります。
s 1100m xxx 指数は00から始まり、仮数は100mで始まる s 1101m xxx 指数は01から始まり、仮数は100mで始まる s 1110m xxx 指数は10から始まり、仮数は100mで始まる
先頭のビット(上記のs)は符号ビットであり、後続のビット(上記のxxx)は追加の指数ビットと最上位桁の残りの部分をエンコードしますが、詳細は使用されるエンコード方式によって異なります。
最終的な組み合わせは無限大とNaNに使用され、どちらの代替エンコーディングでも同じです。
s 11110 x ±無限大 (拡張実数直線を参照) s 11111 0 静かなNaN(符号ビットは無視されます) s 11111 1 は NaN を示しています (符号ビットは無視されます)
後者の場合、エンコードの他のビットはすべて無視されます。したがって、1バイトの値で配列を埋めることで、配列をNaNで初期化することが可能です。
このフォーマットでは、0 から 10 p −1 までのバイナリ仮数を使用します。たとえば、Decimal32 の仮数は、最大で 10 7 −1 = 9 999 999 = 98967F 16 = 1001 1000100101 1001111111 2まで可能です。このエンコーディングではより大きな仮数を表すことができますが、それらは不正であり、標準では、入力で検出された場合は実装で 0 として扱うことが求められています。
上述のように、エンコーディングは仮数の最上位 4 ビットが 0 ~ 7 (0000 2~ 0111 2 ) の範囲内にあるか、それ以上 (1000 2または 1001 2 ) の範囲にあるかによって異なります。
符号ビットの後の 2 ビットが「00」、「01」、または「10」の場合、指数部は符号ビットの後の 8 ビット (上記の 2 ビットと「指数継続フィールド」の 6 ビット) で構成され、仮数は残りの 23 ビットで、括弧内に暗黙の先頭 0 ビットが示されています。
s 00eeeeee (0)ttt tttttttttt tttttttttt s 01eeeeee (0)ttt tttttttttt tttttttttt s 10eeeeee (0)ttt tttttttttt tttttttttt
これには、先頭の有効数字が0であるような、通常とは異なる数値も含まれます。
符号ビットの後の2ビットが「11」の場合、8ビットの指数部は(符号ビットとそれに続く「11」ビットの両方の後)2ビット右にシフトされ、表現される仮数部は残りの21ビットに格納されます。この場合、真の仮数部には暗黙的に(つまり格納されずに)先頭に3ビットのシーケンス「100」が存在します。
s 1100eeeeee (100)t tttttttttt tttttttttt s 1101eeeeee (100)t tttttttttt tttttttttt s 1110eeeeee (100)t tttttttttt tttttttttt
符号ビットの後の「11」という2ビットのシーケンスは、仮数に暗黙的に「100」という3ビットの接頭辞が付いていることを示しています。
仮数フィールドの先頭ビットは、最上位の10進数をエンコードするものではなく、単により大きな純粋な2進数の一部であることに注意してください。たとえば、仮数8 000 000は、先頭の4ビットが7をエンコードする2進数0111 1010000100 1000000000としてエンコードされます。24ビット目を必要とする最初の仮数(したがって2番目のエンコード形式)は2 23 = 8 388 608です。
上記の場合、表される値は次のとおりです。
Decimal64 と Decimal128 は同様に動作しますが、指数部と仮数部のサイズが大きくなっています。Decimal128 の場合、2 番目のエンコード形式は実際には使用されません。有効な仮数の最大値である 10 34 −1 = 1ED09BEAD87C0378D8E63FFFFFFFF 16 は113 ビットで表現できます。
このバージョンでは、仮数は一連の10進数として格納されます。先頭の桁は0から9(3ビットまたは4ビットの2進数)で、残りの仮数は高密度10進数(DPD)エンコーディングを使用します。
指数部の先頭2ビットと仮数の先頭桁(3ビットまたは4ビット)が組み合わされて、符号ビットに続く5ビットが生成される。その後に、固定オフセットの指数継続フィールドが続く。
最後に、仮数継続フィールドは、それぞれが3桁の10進数を符号化する2、5、または11個の10ビットデクレットで構成されます。[ 9 ]
符号ビットの後の最初の 2 ビットが「00」、「01」、または「10」の場合、それらは指数部の先頭ビットであり、その後の 3 ビットは先頭の 10 進数 (0 ~ 7) として解釈されます。[ 10 ]
組み合わせ指数有効数字 s 00 TTT (00)eeeeee (0TTT)[tttttttttt][ttttttttttt] s 01 TTT (01)eeeeee (0TTT)[tttttttttt][ttttttttttt] s 10 TTT (10)eeeeee (0TTT)[tttttttttt][tttttttttt]
符号ビットの次の2ビットが「11」の場合、次の2ビットは指数部の先頭ビットとなり、最後のビットには「100」が付加されて先頭の10進数(8または9)が形成されます。
組み合わせ指数有効数字 s 1100 T (00)eeeeee (100T)[tttttttttt][ttttttttttt] s 1101 T (01)eeeeee (100T)[tttttttttt][ttttttttttt] s 1110 T (10)eeeeee (100T)[tttttttttt][tttttttttt]
5ビットフィールドの残りの2つの組み合わせ(11110と11111)は、それぞれ±無限大とNaNを表すために使用されます。
浮動小数点演算を実行する際の一般的なルールは、正確な数学的値を計算し、[ 11 ]結果を指定された精度で表現可能な最も近い値に丸めることです。これは、通常の丸め動作で例外的な条件がない場合、IEEE 準拠のコンピュータ ハードウェアに義務付けられている動作です。
説明と理解を容易にするため、例では7桁の精度を使用します。ただし、基本的な原理はどの精度でも同じです。
浮動小数点数を加算する簡単な方法は、まず同じ指数で表すことです。以下の例では、2番目の数値を3桁右にシフトしています。通常の加算方法に従って計算を進めます。
以下の例は十進数です。これは、基数が10であることを意味します。
123456.7 = 1.234567 × 10 5 101.7654 = 1.017654 × 10 2 = 0.001017654 × 10 5
したがって:
123456.7 + 101.7654 = (1.234567 × 10 5 ) + (1.017654 × 10 2 ) = (1.234567 × 10 5 ) + (0.001017654 × 10 5 ) = 10 5 × (1.234567 + 0.001017654) = 10 5 × 1.235584654
これは科学的記数法への変換に他なりません。詳細は以下の通りです。
e=5; s=1.234567 (123456.7) + e=2; s=1.017654 (101.7654)
e=5; s=1.234567 + e=5; s=0.001017654 (シフト後) -------------------- e=5; s=1.235584654 (真の合計: 123558.4654)
これが真の結果、つまり各被演算項の正確な合計です。7桁に丸められ、必要に応じて正規化されます。最終結果は次のとおりです。
e=5; s=1.235585 (最終合計: 123558.5)
2番目のオペランドの下位3桁(654)が実質的に失われていることに注意してください。これは丸め誤差です。極端な場合、2つのゼロ以外の数値の合計が、どちらか一方の数値と等しくなることがあります。
e=5; s=1.234567 + e=−3; s=9.876543
e=5; s=1.234567 + e=5; s=0.00000009876543 (シフト後) ---------------------- e=5; s=1.23456709876543 (真の合計) e=5; s=1.234567(丸め/正規化後)
ほぼ等しい2つの数の近似値を引き算すると、有効性が失われるという別の問題が発生します。次の例では、 e = 5; s = 1.234571 とe = 5; s = 1.234567 は、有理数 123457.1467 と 123456.659 の近似値です。
e=5; s=1.234571 − e=5; s=1.234567 ---------------- e=5; s=0.000004 e=−1; s=4.000000(丸めと正規化後)
浮動小数点の差は、数値が近いため正確に計算されます。これは、段階的アンダーフローがサポートされているアンダーフローの場合でも、スターベンツの補題によって保証されます。それにもかかわらず、元の数値の差はe = −1; s = 4.877000 であり、これは近似値の差e = −1; s = 4.000000と 20% 以上異なります。極端な場合、精度の有効桁がすべて失われる可能性があります。[ 12 ] [ 13 ]この相殺は、計算結果のすべての桁が意味を持つと仮定することの危険性を示しています。これらのエラーの結果に対処することは、数値解析のトピックです。精度の問題も参照してください。
乗算を行うには、仮数部同士を掛け合わせ、指数部同士を足し合わせ、結果を四捨五入して正規化します。
e=3; s=4.734612 × e=5; s=5.417242 ----------------------- e=8; s=25.648538980104 (真の積) e=8; s=25.64854(四捨五入後) e=9; s=2.564854(正規化後)
割り算も同様に行いますが、こちらはより複雑です。
乗算や除算には、打ち消しや吸収の問題は発生しませんが、演算を繰り返し行うと小さな誤差が蓄積される可能性があります。実際には、デジタルロジックにおけるこれらの演算の実行方法は非常に複雑になる場合があります。
{{cite book}}: CS1メンテナンス: 場所の発行元が見つかりません (リンク)