次のコードを考えてみましょう:
リーリー リーリーなぜこのような不正確さが生じるのでしょうか?
私は浮動小數(shù)點ハードウェアを設(shè)計および構(gòu)築しているので、ハードウェア設(shè)計者の視點を追加する必要があると考えています。エラーの原因がわかれば、ソフトウェアで何が起こっているのかを理解するのに役立つ可能性があり、最終的には、浮動小數(shù)點エラーが発生し、時間の経過とともに蓄積される理由の説明に役立つことを願っています。
###1。概要###ほとんどのプロセッサは IEEE-754 標(biāo)準(zhǔn)に従っていますが、一部のプロセッサは非正規(guī)化または異なる標(biāo)準(zhǔn)を使用しています。 。たとえば、IEEE-754 には、精度を犠牲にして非常に小さな浮動小數(shù)點數(shù)を表現(xiàn)できる非正規(guī)化モードがあります。ただし、以下では、典型的な動作モードである IEEE-754 の標(biāo)準(zhǔn)化モードについて説明します。
浮動小數(shù)點除算エラーの主な原因は、商の計算に使用される除算アルゴリズムです。ほとんどのコンピュータ システムは、主に Z=X/Y
、Z = X * (1/Y)
などの逆乗算を使用して割り算を計算します。除算は繰り返し計算されます。つまり、必要な精度に達(dá)するまで商のビット數(shù)がサイクルごとに計算されます。IEEE-754 の場合、これは最後のビット誤差が 1 単位未満である任意の値です。 Y の逆數(shù)テーブル (1/Y) は、低速除算では商選択テーブル (QST) と呼ばれます。商選択テーブルのサイズ (ビット単位) は、通常、底の幅、または底の桁數(shù)になります。 。商は反復(fù)ごとに計算され、ガード ビットも加えられます。 IEEE-754 標(biāo)準(zhǔn)の倍精度 (64 ビット) の場合、これは分周器のベースにいくつかのガード ビット k を加えたサイズになります (2 2= 4 ビット (およびいくつかのオプションのビット) になります。
3.1 除算の丸め誤差: 逆數(shù)の近似
商選択テーブルの逆數(shù)は、division: SRT 除算のような遅い除算、または Goldschmidt 除算のような高速除算によって異なります。各エントリは除算アルゴリズムに従って変更され、可能な限り最小値を生成しようとします。間違い。いずれにせよ、すべての逆數(shù)は実際の逆數(shù)の近似値であり、何らかの誤差要素が生じます。低速除算と高速除算はどちらも商を繰り返し計算します。つまり、各ステップで商の桁數(shù)を計算し、その結(jié)果を被除數(shù)から減算します。除算器は、誤差が最後の桁の 2 分の 1 未満になるまでこれらのステップを繰り返します。 。遅い除算メソッドは、各ステップで固定桁數(shù)の商を計算するため、一般に構(gòu)築コストが低くなります。一方、高速除算メソッドは、各ステップで可変桁數(shù)を計算するため、一般に構(gòu)築コストが高くなります。割り算の最も重要な部分は、ほとんどの割り算が逆數(shù)の 近似の繰り返しの乗算に依存しているため、間違いを犯しやすいことです。
###4。他の演算における丸め誤差: 切り捨て四捨五入 (デフォルト)、 切り捨て、切り上げがあります。単一の操作の場合、すべてのメソッドで最後に 1 単位未満のエラー要素が発生します。また、切り捨ては、時間の経過や操作の繰り返しにより最終誤差を累積的に増加させます。この切り捨て誤差は、べき乗に何らかの形式の乗算の繰り返しが含まれる場合に特に問題になります。 ###5。操作を繰り返します
に丸められ、1 回の演算での最後の桁の誤差が半分未満になることに注意してください。切り捨て、切り上げ、および切り捨てを単獨で行うと、最後の桁の 2 分の 1 より大きく、最後の桁の 1 単位未満の誤差が生じる可能性があるため、これらのモードは區(qū)間演算以外には推奨されません。 ###6。まとめ### a>つまり、浮動小數(shù)點演算におけるエラーの根本原因は、除算時のハードウェア切り捨てと逆數(shù)切り捨ての組み合わせです。 IEEE-754 標(biāo)準(zhǔn)では、単一演算の最後のビットの誤差が 2 分の 1 未満であることだけが要求されているため、繰り返し演算による浮動小數(shù)點誤差は修正されない限り蓄積されます。
バイナリ浮動小數(shù)點計算は次のようになります。ほとんどのプログラミング言語では、IEEE 754 標(biāo)準(zhǔn)に基づいています。問題の核心は、この形式では數(shù)値が整數(shù)の 2 のべき乗、つまり分母が 2 のべき乗ではない有理數(shù) (1/10# である
0.1 など) として表現(xiàn)されることです。 ##) を正確に表現(xiàn)できません。
binary64 形式
0.1 の場合、その表現(xiàn)は
とまったく同じように書くことができます。
(10 進(jìn)數(shù))、または
C99 16 進(jìn)浮動小數(shù)點表現(xiàn) .
0.1 (
1/10) は、
と正確に書くことができます。
(10 進(jìn)數(shù))、または
は C99 の 16 進(jìn)浮動小數(shù)點表記に似ており、
... は無限に続く 9 を表します。
0.2 および
0.3 も、実際の値の近似値になります。たまたま、
0.2 に最も近い
double は有理數(shù)
0.2 よりも大きいですが、最も近い
double code>0.3
は有理數(shù) 0.3 より小さいです。
0.1 と
0.2 の合計は、有理數(shù)
0.3 よりも大きくなるため、コード內(nèi)の定數(shù)と矛盾します。
すべてのコンピュータ科學(xué)者は浮動小數(shù)點演算について知っておくべきです。よりわかりやすい説明については、floating-point-gui.de を參照してください。
同じ問題が普通の 10 進(jìn)數(shù) (基數(shù) 10) にも存在します。そのため、1/3 のような數(shù)値は 0.333333333...になります。
あなたは、10 進(jìn)數(shù)では簡単に表現(xiàn)できますが、2 進(jìn)數(shù)では表現(xiàn)できない數(shù)値 (3/10) を見つけました。また、これは (ある程度) 両方の方向に當(dāng)てはまります。1/16 は、10 進(jìn)數(shù)では醜い數(shù)字 (0.0625) ですが、2 進(jìn)數(shù)では、10,000 番目の 10 進(jìn)數(shù) (0.0001)** のように見えます。日常生活の中で 2 を基數(shù)とする數(shù)値體系を使用する習(xí)慣があれば、その數(shù)値を見て、何かを半分にし、それを何度も何度も半分にすることでその數(shù)値に到達(dá)できることを直感的に理解できるようになります。もちろん、これは浮動小數(shù)點數(shù)がメモリに格納される方法とまったく同じではありません (浮動小數(shù)點數(shù)は科學(xué)的表記法を使用します)。ただし、これは、私たちが通常関心を持っている「現(xiàn)実世界」の數(shù)値は通常 10 のべき乗であるため、2 進(jìn)浮動小數(shù)點の精度エラーが発生する傾向があることを示しています。ただし、それは単に私たちが現(xiàn)在 10 進(jìn)數(shù)體系を使用しているからです。これが、「7 個中 5 個」ではなく 71% と言う理由です (5/7 は 10 進(jìn)數(shù)で正確に表すことができないため、71% は近似値です)。
だからいいえ: 2 進(jìn)浮動小數(shù)點數(shù)は壊れているのではなく、他のすべての N ベースの數(shù)値體系と同じようにたまたま不完全であるだけです :)
実際には、この精度の問題は、浮動小數(shù)點數(shù)を表示する前に、丸め関數(shù)を使用して浮動小數(shù)點數(shù)を目的の小數(shù)點以下の桁數(shù)に丸める必要があることを意味します。
また、同等性テストを、ある程度の許容範(fàn)囲を許容する比較に置き換える必要があります。これは、次のことを意味します。
Don'tDon'tDoif (x == y) { ... }
代わりに、if (abs(x - y) を?qū)g行します。
ここで、abs
は絶対値です。 myToleranceValue
選択は、特定のアプリケーションに基づいて行う必要があります。これは、許容する準(zhǔn)備ができている「余?!工?、(精度のため) 比較する最大値が何になるかに大きく関係します。損失の問題)。選択した言語の「イプシロン」スタイル定數(shù)に注意してください。これらの は許容値として使用できますが、大きな數(shù)値の計算はイプシロンのしきい値を超える可能性があるため、その有効性は扱う數(shù)値のサイズ (サイズ) によって異なります。