フロートの不正確さが原因の不平等


15

少なくともJavaでは、このコードを書くと:

float a = 1000.0F;
float b = 0.00004F;
float c = a + b + b;
float d = b + b + a;
boolean e = c == d;

の値はeなり。これは、数値を正確に表現する上でフロートが非常に制限されているという事実が原因であると考えています。しかし、なぜの位置を変えるだけでこの不平等が生じるのか理解できません。falsea

以下のように、3行目と4行目の両方で sを1 に減らしましたが、の値はなり。betrue

float a = 1000.0F;
float b = 0.00004F;
float c = a + b;
float d = b + a;
boolean e = c == d;

3行目と4行目で何が起こったのでしょうか?浮動小数点数を使用した加算演算が結合的ではないのはなぜですか?

前もって感謝します。


16
例が示すように、浮動小数点の加算可換です。しかし、それは連想的ではありません。
ユヴァルフィルマス

1
基本的な定義を調べることをお勧めします。また、コンパイラーはr+s+t(r+s)+t(左側に追加されるアソシエーションとして解析することにも注意してください。
ユヴァルフィルマス

2
なぜそうなのかを簡単に確認する方法として、などの非常Xに大きな数とY非常に小さな数を検討してくださいX + Y = X。ここでX + Y + -Xは、ゼロになります。しかし、X + -X + YなりますY
デビッドシュワルツ


回答:


20

典型的な浮動小数点の実装では、単一の演算の結果は、演算が無限の精度で実行されたかのように生成され、最も近い浮動小数点数に丸められます。

比較+のB及びB +:無限の精度で実行された各演算の結果は、したがって、これらの同一の無限精度の結果を同じように丸みを帯びている、同じです。つまり、浮動小数点加算は可換です。a+bb+a

テイクBは浮動小数点数です。バイナリ浮動小数点数、2 (b)はまた、浮動小数点数(指数がいずれかによって大きくなる)ので、B + Bが、任意の丸め誤差なしで添加されます。次に、a正確なb + bに追加されます。結果は、最も近い浮動小数点数に丸められた正確な2 b + aです。b+b+ab2bb+bab+b2b+a

テイク+ B + Bを+のBが追加され、丸めエラーが発生しますR我々は結果を得るため、A + B + Rbを追加すると、結果は正確な2 b + a + rになり、最も近い浮動小数点数に丸められます。a+b+ba+bra+b+rb2b+a+r

したがって、ある場合には、、丸められます。他の場合、2 b + a + r、丸められます。2b+a2b+a+r

PS。2つの特定の数値bの両方の計算で同じ結果が得られるかどうかは、数値、および計算の丸め誤差a + bに依存し、通常は予測が困難です。原則として単精度または倍精度を使用しても問題に違いはありませんが、丸め誤差が異なるため、単精度では結果が等しく、倍精度では結果が等しくない、またはその逆のaとbの値があります。精度ははるかに高くなりますが、2つの式が数学的に同じであるが浮動小数点演算で同じではないという問題は同じままです。aba+b

PPS。一部の言語では、実際のステートメントで指定されるよりも高い精度または高い範囲の数値で浮動小数点演算が実行される場合があります。その場合、両方の合計が同じ結果になる可能性がはるかに高くなります(ただし、保証はされません)。

PPPS。コメントは、浮動小数点数が等しいかどうかを尋ねるべきかどうかを尋ねました。絶対にあなたが何をしているか知っている場合。たとえば、配列をソートしたり、セットを実装したりすると、「ほぼ等しい」という概念を使用したい場合、ひどいトラブルに巻き込まれます。グラフィカルユーザーインターフェイスでは、オブジェクトのサイズが変更された場合、オブジェクトサイズを再計算する必要がある場合があります-oldSize == newSizeを比較して、再計算を回避します。不要な再計算があったとしても。


この特定の場合、bはバイナリに変換されると周期的になるため、どこでも丸め誤差があります。
アンドレスーザレモス

1
bこの回答の@AndréSouzaLemos は0.00004ではなく、変換および丸め後に得られるものです。
アレクセイロマノフ

「典型的な浮動小数点の実装では、単一の演算の結果は、演算が無限の精度で実行されたかのように生成され、次に最も近い浮動小数点数に丸められます。」論理ゲートの観点からこれを実際に実装しようとしたとき(シミュレータは64ビットバスしか処理できませんでした)。
ジョンドヴォルザーク

素朴な質問:floatの等価性をテストすることは理にかなっていますか?ほとんどのプログラミング言語では、両方または一方が浮動小数点であるaa == bテストを許可するのはなぜですか?
curious_cat

ウィキペディアの関連定義:「マシンイプシロンは、浮動小数点演算の丸めによる相対誤差の上限を示します。」
ブラックホーク

5

コンピューターでサポートされているバイナリ浮動小数点形式は、人間が使用する10進数の科学表記法に本質的に似ています。

浮動小数点数は、次のように、符号、仮数(固定幅)、および指数(固定幅)で構成されます。

+/-  1.0101010101 × 2^12345
sign   ^mantissa^     ^exp^

通常の科学表記法の形式は次のとおりです。

+/- 1.23456 × 10^99

科学演算で有限精度の算術演算を行い、各演算の後に丸めると、2進浮動小数点と同じ悪影響があります。


説明のために、小数点の後に正確に3桁を使用するとします。

a = 99990 = 9.999 × 10^4
b =     3 = 3.000 × 10^0

(a + b)+ b

ここで計算します:

c = a + b
  = 99990 + 3      (exact)
  = 99993          (exact)
  = 9.9993 × 10^4  (exact)
  = 9.999 × 10^4.  (rounded to nearest)

もちろん、次のステップでは:

d = c + b
  = 99990 + 3 = ...
  = 9.999 × 10^4.  (rounded to nearest)

したがって、(a + b)+ b = 9.999×10 4

(b + b)+ a

しかし、異なる順序で操作を行った場合:

e = b + b
  = 3 + 3  (exact)
  = 6      (exact)
  = 6.000 × 10^0.  (rounded to nearest)

次に計算します:

f = e + a
  = 6 + 99990      (exact)
  = 99996          (exact)
  = 9.9996 × 10^4  (exact)
  = 1.000 × 10^5.  (rounded to nearest)

したがって、(b + b)+ a = 1.000×10 5で、これは他の答えとは異なります。


5

JavaはIEEE 754 2進浮動小数点表現を使用します。これは仮数に23の2進数字を割り当て、最初の有効数字で始まるように正規化されます(スペースを節約するために省略)。

0.0000410=0.00000000000000101001111100010110101100010001110001101101000111 ...2=[1。]01001111100010110101100010001110001101101000111 ...2×215

100010+0.0000410=1111101000.00000000000000101001111100010110101100010001110001101101000111 ...2=[1。]11110100000000000000000101001111100010110101100010001110001101101000111 ...2×29

赤の部分は仮数です。実際には(丸め前)表現されています。

(100010+0.0000410)+0.0000410(0.0000410+0.0000410)+100010


0

最近、同様の丸めの問題に遭遇しました。上記の答えは正しいですが、非常に技術的です。

丸めエラーが存在する理由については、次の説明が適切な説明であることがわかりました。 http://csharpindepth.com/Articles/General/FloatingPoint.aspx

TLDR:2進浮動小数点は、10進浮動小数点に正確にマッピングできません。これにより、数学的操作中に不正確になる可能性があります。

10進数の浮動小数点数を使用する例:1/3 + 1/3 + 1/3は通常1に等しくなります。ただし、10進数では0.333333 + 0.333333 + 0.333333が1.000000と完全に等しくなることはありません。

2進数の10進数で数学演算を行う場合も同じことが起こります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.