ホームページ  >  に質問  >  本文

浮動小数点演算に問題があるのでしょうか?

<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>
P粉330232096P粉330232096400日前484

全員に返信(2)返信します

  • P粉041856955

    P粉0418569552023-08-22 13:16:54

    ハードウェア設計者の視点

    私は浮動小数点ハードウェアを設計および構築しているので、ハードウェア設計者の観点からいくつかの視点を追加したいと考えました。エラーの原因を知ることは、ソフトウェアで何が起こっているのかを理解するのに役立つ可能性があり、最終的には、浮動小数点エラーが発生し、時間の経過とともに蓄積される理由の説明に役立つことを願っています。

    ###1。概要###

    エンジニアリングの観点から見ると、浮動小数点計算を実行するハードウェアは最後のビットの半分より下に 1 単位の誤差があれば十分であるため、ほとんどの浮動小数点演算には何らかの誤差が生じます。したがって、ほとんどのハードウェアは、1 回の演算で最終ビットの誤差の半分未満を生成するだけで十分な精度で停止しますが、これは浮動小数点除算では特に注意が必要です。 1 つの演算を構成するものは、ユニットが受け入れるオペランドの数によって異なります。ほとんどのユニットでは 2 ですが、一部のユニットは 3 つ以上のオペランドを受け入れます。したがって、これらのエラーは時間の経過とともに蓄積されるため、繰り返し操作によって理想的なエラーが生成されるという保証はありません。

    2.標準

    ほとんどのプロセッサは

    IEEE-754

    標準に従っていますが、一部のプロセッサは非正規化された標準または異なる標準を使用します。たとえば、IEEE-754 には、精度を犠牲にして非常に小さな浮動小数点数を表現できる非正規化モードがあります。ただし、以下では代表的な動作モードであるIEEE-754の正規化モードについて説明します。 IEEE-754 標準では、ハードウェア設計者は、最後のビットの単位の半分未満である限り、任意のエラー/イプシロン値を選択でき、結果は最後のビットの単位の半分未満であることのみが必要です。 1回の操作で。これは、操作を繰り返すとエラーが蓄積される理由を説明します。 IEEE-754 倍精度の場合、仮数とも呼ばれる浮動小数点数の数値部分 (正規化部分) を表すために 53 ビットが使用されるため、これはビット 54 です (例: 5.3e5 の 5.3)。次のいくつかのセクションでは、さまざまな浮動小数点演算におけるハードウェア エラーの原因を詳しく見ていきます。

    3. 除算と丸め誤差の原因

    浮動小数点除算におけるエラーの主な原因は、商の計算に使用される除算アルゴリズムです。ほとんどのコンピュータ システムは、主に

    Z=X/Y

    および Z = X * (1/Y) で、乗算の逆関数を使用して除算を計算します。除算は繰り返し計算されます。つまり、必要な精度 (IEEE-754 の場合は最後の桁の誤差の半分未満) に達するまで、商の一部の桁数がサイクルごとに計算されます。 Y の逆数テーブル (1/Y) は、低速除算では商選択テーブル (QST) と呼ばれます。商選択テーブルの桁数は、通常、底の幅、または各反復で計算される商の数に加算した値になります。いくつかのガードビットがあります。 IEEE-754 標準の倍精度 (64 ビット) の場合、除算器の基本サイズにいくつかのガード ビット k を加えたものになります (k>=2)。したがって、たとえば、一度に 2 ビット (基数 4) で商を計算する一般的な商選択テーブルは、2 2 = 4 ビット (およびいくつかのオプションのビット) になります。

    3.1 除算の丸め誤差: 逆数の近似

    商選択テーブルの逆数は、除算方法によって異なります: 低速除算 (SRT 除算など) または高速除算 (ゴールドシュミット除算など)。各エントリは、除算アルゴリズムに従って変更されます。誤差を最小限に抑えます。いずれにせよ、すべての逆数は実際の逆数の 近似であり、特定の誤差が導入されます。低速除算と高速除算のどちらの方法でも、商を繰り返し計算します。つまり、各ステップで特定の桁数の商を計算し、その結果を被除数から減算し、除数は誤差が最後の半分未満になるまでこれらのステップを繰り返します。桁。遅い除算方法は、各ステップで固定数の商の桁を計算するため、一般的に安価ですが、高速の除算方法は、各ステップで可変数の商の桁を計算するため、一般に高価です。除算方法で最も重要な点は、そのほとんどが の逆数を近似することによる乗算の繰り返しに依存しているため、エラーが発生しやすいことです。

    4. 他の演算における丸め誤差: 切り捨て

    すべての演算で丸め誤差が発生するもう 1 つの原因は、IEEE-754 で許可されているさまざまな切り捨てモードです。切り捨て、ゼロ方向に四捨五入、

    最も近い値に丸める (デフォルト) 、切り捨て、および切り上げがあります。すべての方法で、1 回の操作で発生するエラーは最後のビットの半分未満です。時間の経過と操作の繰り返しにより、切り捨ても累積してエラーが発生します。この切り捨てエラーは、何らかの形式の乗算を繰り返す指数演算の場合に特に問題になります。

    5. 操作を繰り返します

    浮動小数点計算を実行するハードウェアは、1 回の演算でユニットの最後のビットの誤差が半分未満の結果を生成するだけでよいため、これを守らないと、演算を繰り返すにつれて誤差が増加します。このため、有限誤差が必要な計算では、数学者は IEEE-754 の

    Nearest Rounding to Even Numbers などの方法を使用します。これは、時間の経過とともに誤差が互いに打ち消し合う可能性が高くなるためです。 arithmeticIEEE 754 丸めモード のバリアントと組み合わせて、丸め誤差を予測し、修正します。最も近い偶数桁 (最後のビット) への丸めは、他の丸めモードと比較して相対誤差が低いため、IEEE-754 のデフォルトの丸めモードです。 デフォルトの丸めモード (最も近い偶数桁に丸める) では、最後の桁の 2 分の 1 単位未満の誤差での演算が保証されることに注意してください。切り捨て、切り上げ、切り捨てのみを使用すると、最後の桁の半分単位より大きく、最後の桁の 1 単位未満の誤差が生じる可能性があるため、間隔で計算している場合を除きます。

    返事
    0
  • P粉041881924

    P粉0418819242023-08-22 11:54:59

    バイナリ浮動小数点数操作は次のようになります。ほとんどのプログラミング言語では、IEEE 754 標準に基づいています。問題の核心は、この形式では数値が整数と 2 の累乗の積、つまり分母が 2 の累乗ではない有理数 (0.1 のように、##1 を掛けたもの) として表されることです。 /10) を正確に表現できません。

    標準の

    binary64 形式の 0.1 の場合、表現は とまったく同じように書くことができます。

    • 0.1000000000000000055511151231257827021181583404541015625 (10 進数)、または
    • 0x1.999999999999ap-4 (C99 16 進浮動小数点表記 )。
    対照的に、有理数

    0.1 (1/10) は、 と正確に書くことができます。

    • 0.1 (10 進数)、または
    • 0x1.99999999999999...p-4 (C99 の 16 進浮動小数点表記に似ており、... は無限の 9 のシーケンスを表します)。
    プログラム内の定数

    0.2 および 0.3 も、実際の値の近似値になります。まさに 0.2 最も近い double は有理数 0.2 より大きいですが、最も近い double は有理数 より小さいです0.30.10.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/10000 (0.0001)** のようにきれいに見えます** - 2 進数の使用に慣れていた場合私たちの日常生活のシステムでは、その数字を見ても、常に半分に折ることで得られることが直感的に理解できるでしょう。

    もちろん、これは浮動小数点数がメモリに格納される方法とまったく同じではありません (浮動小数点数は科学表記法を使用します)。ただし、これは、私たちが通常関心を持っている「現実世界」の数値は 10 のべき乗である傾向があるため、2 進浮動小数点の精度エラーが発生する傾向があるという問題を示していますが、それは私たちが日常的に 10 進数体系を使用しているからにすぎません。これが、「7 個中 5 個」ではなく 71% と言う理由です (5/7 を正確に表すことができる 10 進数はないため、71% は近似値です)。

    いいえ、2 進浮動小数点数は壊れているのではなく、他の N 進数体系と同様に不完全なだけです :)

    実際には、この精度の問題は、浮動小数点数を表示する前に、丸め関数を使用して、浮動小数点数を目的の小数点以下の桁数に丸める必要があることを意味します。

    また、等価性テストを、一定の許容差を許容する比較に置き換える必要があります。これは、次のことを意味します。

    if (x == y) { ... }

    は使用しないでください。

    代わりに、if (abs(x - y) < myToleranceValue) { ... } を使用してください。

    ここで、abs は絶対値関数です。 myToleranceValue は、特定のアプリケーションに基づいて選択する必要があります。これは、どの程度の「余裕」を許容するか、および比較する最大値 (精度の損失による) に大きく関係します。問題)。選択した言語の「イプシロン」スタイル定数に注意してください。これらの定数は許容値として使用できますが、その有効性は扱う数値のサイズによって異なります (大きな数値の計算ではイプシロンのしきい値を超える可能性があるため)。

    返事
    0
  • キャンセル返事