浮動小数点の計算に問題があるのでしょうか?
P粉071602406
2023-08-20 16:48:26
<p>次のコードを考えてみましょう。</p>
<pre class="brush:js;toolbar:false;">0.1 0.2 == 0.3 -> false
</pre>
<pre class="brush:js;toolbar:false;">0.1 0.2 -> 0.30000000000000004
</pre>
<p>このような不正確さはなぜ起こるのでしょうか? </p>
ハードウェア設計者の視点
私は浮動小数点ハードウェアを設計および構築しているので、ハードウェア設計者の視点を追加する必要があると考えました。エラーの原因を知ることは、ソフトウェアで何が起こっているのかを理解するのに役立つ可能性があり、最終的には、浮動小数点エラーが発生し、時間の経過とともに蓄積される理由がこれによって説明されることを願っています。
###1。概要###エンジニアリングの観点から見ると、浮動小数点計算を実行するハードウェアが必要とする誤差は最後のビットの半分未満のユニットだけであるため、ほとんどの浮動小数点演算にはある程度の誤差が生じます。したがって、ほとんどのハードウェアは精度のみで停止し、最終ビットの半分単位未満の誤差を生成するだけで済みます。これは浮動小数点除算で特に問題となります。 1 つの演算を構成するものは、ユニットが受け入れるオペランドの数によって異なります。ほとんどのユニットでは、これは 2 つのオペランドですが、一部のユニットは 3 つ以上のオペランドを受け入れます。したがって、時間の経過とともにエラーが蓄積されるため、繰り返し操作によって理想的なエラーが生成されるという保証はありません。
2.標準
ほとんどのプロセッサは
IEEE-754標準に従っていますが、一部のプロセッサは非正規化された標準または異なる標準を使用します。たとえば、IEEE-754 には、精度を犠牲にして非常に小さな浮動小数点数の表現を許可する非正規化モードがあります。ただし、次の内容では、標準的な動作モードである IEEE-754 の正規化モードについて説明します。 IEEE-754 標準では、ハードウェア設計者は、最後のビットの半分単位未満である限り、error/ε の任意の値を許可でき、結果は最後のビットの半分単位未満でなければなりません。 1回の操作で。これは、操作が繰り返されるとエラーが蓄積される理由を説明します。 IEEE-754 倍精度の場合、仮数とも呼ばれる浮動小数点数の数値部分 (正規化) を表すために 53 ビットが使用されるため、これはビット 54 です (例: 5.3e5 の 5.3)。次のセクションでは、さまざまな浮動小数点演算におけるハードウェア エラーの原因について詳しく説明します。
3. 割り算における丸め誤差の原因
浮動小数点除算におけるエラーの主な原因は、商の計算に使用される除算アルゴリズムです。ほとんどのコンピュータ システムでは、主に
Z=X/Yおよび
3.1 除算の丸め誤差: 逆数の近似Z = X * (1/Y)
で除算の計算に逆乗法を使用します。除算は反復的に計算されます。つまり、必要な精度に達するまでサイクルごとに商の桁数が計算されます。IEEE-754 の場合、この精度は最後の桁の 1 単位未満の誤差です。 Y の逆数表 (1/Y) は、低速除算では商選択テーブル (QST) と呼ばれます。商選択テーブルの桁数は、通常、底の幅、または で計算された商の桁数になります。各反復に保護位置の数を加えたもの。 IEEE-754 標準の倍精度 (64 ビット) の場合、除算器の基本サイズにいくつかのガード ビット k を加えたものになります (k>=2
)。したがって、たとえば、一度に 2 ビットの商 (基数 4) を計算する一般的な除算器の商選択テーブルは、2 2= 4
ビット (およびいくつかのオプションのビット) になります。商選択テーブルの逆数は、 除算方法 : 低速除算 (SRT 除算など) または高速除算 (ゴールドシュミット除算など) によって異なります。各エントリは、除算アルゴリズムに従って変更されます。できる限り誤差を最小限に抑えます。いずれにしても、すべての逆数は実際の逆数の 近似であり、ある程度の誤差が生じます。遅い除算方法と速い除算方法はどちらも商を繰り返し計算します。つまり、各ステップで特定の数の商の桁が計算され、その結果が被除数から減算され、除数は誤差が単位の半分未満になるまでこれらのステップを繰り返します。最後の桁。遅い除算方法は、各ステップで固定数の商の桁を計算するため、一般的に安価ですが、高速の除算方法は、各ステップで可変数の商の桁を計算するため、一般に高価です。除算法の最も重要な点は、そのほとんどが逆数の 近似の繰り返しの乗算に依存しているため、エラーが発生しやすいことです。
4. 他の演算における丸め誤差: 切り捨て
すべての演算で丸め誤差が発生するもう 1 つの原因は、IEEE-754 で許可されているさまざまな切り捨てモードです。切り捨て、ゼロ方向丸め、round (デフォルト) 、切り捨て、および切り上げがあります。すべてのメソッドで、1 回の操作で発生するエラーは最後のビットの単位未満です。時間の経過と操作の繰り返しにより、切り捨ても累積してエラーが発生します。この切り捨て誤差は、何らかの形式で乗算を繰り返す指数演算において特に問題となります。
5. 操作を繰り返します
浮動小数点計算を実行するハードウェアは、単一の演算で最後のビットの誤差単位の半分未満の結果を生成するだけでよいため、監視していないと、演算が繰り返されるにつれて誤差が増大します。有限誤差が必要な計算では、数学者が IEEE-754 を使用して最後の桁を偶数桁に丸めるなどの方法を使用するのはこのためです。時間の経過とともに誤差が互いに影響し合う可能性が高くなるためです。オフセットと Interval の変更を組み合わせます。算術およびIEEE 754丸めモードを使用して丸め誤差を予測し、修正します。最も近い偶数桁 (最後の桁) への丸めは、他の丸めモードと比較して相対誤差が低いため、IEEE-754 のデフォルトの丸めモードです。 デフォルトの丸めモード (最も近い
偶数桁に丸める) では、演算の誤差が最後の桁の半分未満であることが保証されることに注意してください。切り捨て、切り上げ、切り捨てのみを使用してください
バイナリ浮動小数点これが数学の仕組みです。ほとんどのプログラミング言語では、IEEE 754 標準に基づいています。問題の核心は、この形式では数値が整数と 2 の累乗の積、つまり分母が 2 の累乗ではない有理数 (
標準の0.1
のように、##1 を掛けたもの) として表されることです。 /10) 正確な表現ができません。
binary64
形式の
0.1の場合、その表現は
として正確に記述できます。10 進数:- 0.1000000000000000055511151231257827021181583404541015625
- C99 hexfloat 記法 :0x1.99999999999ap-4
対照的に、有理数0.1
(
1/10) は、
と正確に書くことができます。10 進数:- 0.1
は、C99 の 16 進数浮動小数点表記の表現 - 0x1.99999999999999...p-4
プログラム内の定数に似ています。ここで、
...は、9 の無限のシーケンスを表します。
0.2
すべてのコンピュータ科学者が浮動小数点演算について知っておくべきことおよび
0.3も、実際の値に近似します。正確に、
0.2に最も近い
doubleは有理数
0.2よりも大きいですが、
doubleは
0.3## に最も近いです。 # は有理数 ##0.3より小さいです。
0.1と
0.2の合計は、有理数
0.3よりも大きくなるため、コード内の定数と矛盾します。
浮動小数点演算のかなり包括的な扱いは、
です。よりわかりやすい説明については、floating-point-gui.de を参照してください。 通常の 10 進数 (基数 10) にも同じ問題が存在します。そのため、1/3 のような数値は 0.333333333... になります。
あなたは、10 進法では簡単に表現できるが、2 進法では表現できない数値 (3/10) に遭遇しました。逆も (ある程度は) 当てはまります。1/16 は、10 進数では醜い数 (0.0625) ですが、2 進数では、10 進数の 1 万分の 1 (0.0001) と同じくらいきれいに見えます* * - 使用に慣れていれば私たちの日常生活で 2 進数体系を使用している場合、その数値を見ただけで、常に 2 で割れば求められることが直感的に理解できるでしょう。代わりに、
if (abs(x - y) < myToleranceValue) { ... }
を使用してください。ここで、
abs
は絶対値関数です。myToleranceValue
は、特定のアプリケーションに基づいて選択する必要があります。これは、どの程度の「余裕」を許容するか、および (精度の損失による) 比較する最大数に大きく関係します。問題)。選択した言語の「イプシロン」スタイル定数に注意してください。これらの定数は許容値として使用できますが、大きな数値を使用した計算ではイプシロンのしきい値を超える可能性があるため、その有効性は扱う数値のサイズによって異なります。