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

浮動小数点演算に問題がありますか?

次のコードを考えてみましょう:

リーリー リーリー

なぜこのような不正確さが生じるのでしょうか?

P粉827121558P粉827121558397日前497

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

  • P粉818561682

    P粉8185616822023-10-12 11:11:47

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

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

    ###1。概要###

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

    ###2。標準###

    ほとんどのプロセッサは IEEE-754 標準に従っていますが、一部のプロセッサは非正規化または異なる標準を使用しています。 。たとえば、IEEE-754 には、精度を犠牲にして非常に小さな浮動小数点数を表現できる非正規化モードがあります。ただし、以下では、典型的な動作モードである IEEE-754 の標準化モードについて説明します。

    IEEE-754 標準では、ハードウェア設計者は、最後の桁の単位の 2 分の 1 未満である限り、任意のエラー/イプシロン値を使用することが許可されており、結果が 1 未満である必要があるだけです。ユニットの半分、操作の最後の位置。これは、操作が繰り返されるとエラーが蓄積される理由を説明します。 IEEE-754 倍精度の場合、これはビット 54 です。これは、仮数としても知られる浮動小数点数の数値部分 (正規化された) を表すために 53 ビットが使用されるためです (例: 5.3e5 の 5.3)。次のセクションでは、さまざまな浮動小数点演算におけるハードウェア エラーの原因について詳しく説明します。

    ###3。除算の丸め誤差の原因

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

    Z = X * (1/Y)

    などの逆乗算を使用して割り算を計算します。除算は繰り返し計算されます。つまり、必要な精度に達するまで商のビット数がサイクルごとに計算されます。IEEE-754 の場合、これは最後のビット誤差が 1 単位未満である任意の値です。 Y の逆数テーブル (1/Y) は、低速除算では商選択テーブル (QST) と呼ばれます。商選択テーブルのサイズ (ビット単位) は、通常、底の幅、または底の桁数になります。 。商は反復ごとに計算され、ガード ビットも加えられます。 IEEE-754 標準の倍精度 (64 ビット) の場合、これは分周器のベースにいくつかのガード ビット k を加えたサイズになります (

    k>=2

    )。たとえば、一度に 2 ビットの商 (基数 4) を計算する除算器の典型的な商選択テーブルは、

    2 2= 4 ビット (およびいくつかのオプションのビット) になります。 3.1 除算の丸め誤差: 逆数の近似

    商選択テーブルの逆数は、division: SRT 除算のような遅い除算、または Goldschmidt 除算のような高速除算によって異なります。各エントリは除算アルゴリズムに従って変更され、可能な限り最小値を生成しようとします。間違い。いずれにせよ、すべての逆数は実際の逆数の近似値であり、何らかの誤差要素が生じます。低速除算と高速除算はどちらも商を繰り返し計算します。つまり、各ステップで商の桁数を計算し、その結果を被除数から減算します。除算器は、誤差が最後の桁の 2 分の 1 未満になるまでこれらのステップを繰り返します。 。遅い除算メソッドは各ステップで固定桁数の商を計算するため、一般に構築コストが低くなります。一方、高速除算メソッドは各ステップで可変桁数を計算するため、一般に構築コストが高くなります。割り算の最も重要な部分は、ほとんどの割り算が逆数の 近似の繰り返しの乗算に依存しているため、間違いを犯しやすいことです。

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

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

    四捨五入 (デフォルト)、 切り捨て、切り上げがあります。単一の操作の場合、すべてのメソッドで最後に 1 単位未満のエラー要素が発生します。また、切り捨ては、時間の経過や操作の繰り返しにより最終誤差を累積的に増加させます。この切り捨て誤差は、べき乗に何らかの形式の乗算の繰り返しが含まれる場合に特に問題になります。 ###5。操作を繰り返します

    浮動小数点計算を実行するハードウェアは、1 回の演算で最後のビットの 2 分の 1 未満の誤差の結果を生成するだけでよいため、注意しないと演算を繰り返すと誤差が増加します。このため、境界誤差が必要な計算では、数学者は最も近い IEEE のバリアント 754 丸めモード と組み合わされて、丸め誤差を予測して修正されます。彼ら。最も近い偶数桁 (最後の桁) への丸めは、他の丸めモードと比較して相対誤差が低いため、IEEE-754 のデフォルトの丸めモードです。 デフォルトの丸めモードでは、最後の桁の最も近い

    偶数

    に丸められ、1 回の演算での最後の桁の誤差が半分未満になることに注意してください。切り捨て、切り上げ、および切り捨てを単独で行うと、最後の桁の 2 分の 1 より大きく、最後の桁の 1 単位未満の誤差が生じる可能性があるため、これらのモードは区間演算以外には推奨されません。 ###6。まとめ### つまり、浮動小数点演算におけるエラーの根本原因は、除算時のハードウェア切り捨てと逆数切り捨ての組み合わせです。 IEEE-754 標準では、単一演算の最後のビットの誤差が 2 分の 1 未満であることだけが要求されているため、繰り返し演算による浮動小数点誤差は修正されない限り蓄積されます。

    返事
    0

  • P粉605385621

    P粉6053856212023-10-12 10:31:38

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

    標準の

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

    • 0.10000000000000000055511151231257827021181583404541015625 (10 進数)、または
    • 0x1.999999999999ap-4 C99 16 進浮動小数点表現 .< /a>
    対照的に、有理数

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

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

    0.2 および 0.3 も、実際の値の近似値になります。たまたま、0.2 に最も近い double は有理数 0.2 よりも大きいですが、最も近い double code>0.3 は有理数 0.3 より小さいです。 0.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,000 番目の 10 進数 (0.0001)** のように見えます。日常生活の中で 2 を基数とする数値体系を使用する習慣があれば、その数値を見て、何かを半分にし、それを何度も何度も半分にすることでその数値に到達できることを直感的に理解できるようになります。

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

    だからいいえ: 2 進浮動小数点数は壊れているのではなく、他のすべての N ベースの数値体系と同じようにたまたま不完全であるだけです :)

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

    また、同等性テストを、ある程度の許容範囲を許容する比較に置き換える必要があります。これは、次のことを意味します。

    Don'tDon'tDoif (x == y) { ... }

    代わりに、if (abs(x - y) < myToleranceValue) { ... }< myToleranceValue) { ... } を実行します。

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

    返事
    0
  • キャンセル返事