まず、浮動小数点値の動作は「ランダム」ではありません。正確な比較は、実際の多くの使用法で有効であり、実際に使用できます。しかし、浮動小数点を使用する場合は、それがどのように機能するかを認識する必要があります。浮動小数点が実数のように機能すると仮定する側を誤ると、すぐに壊れるコードが得られます。浮動小数点の結果に大きなランダムファズが関連付けられていると想定する側(ここでのほとんどの回答が示唆するように)に誤りを犯すと、最初は機能しているように見えるが、最終的には大きなエラーと壊れたコーナーケースが発生するコードが得られます。
まず第一に、浮動小数点でプログラムしたいなら、これを読むべきです:
すべてのコンピューター科学者が浮動小数点演算について知っておくべきこと
はい、すべてお読みください。負担が大きすぎる場合は、整数/固定小数点を使用して、読み取る時間があるまで計算を行う必要があります。:-)
さて、そうは言っても、正確な浮動小数点比較の最大の問題は次のようになります。
値の多くは、あなたがソースに書き込み、またはにして読むことができるという事実scanf
やstrtod
、存在していない浮動小数点値として静かに最も近い近似値に変換します。これは、demon9733の答えが話していたものです。
実際の結果を表すのに十分な精度がないため、多くの結果が丸められるという事実。あなたはこれが追加されて見ることができる簡単な例x = 0x1fffffe
とy = 1
山車として。ここでx
は、仮数部に24ビットの精度(ok)がありy
、1ビットしかありませんが、それらを追加すると、それらのビットは重複する場所になく、結果には25ビットの精度が必要になります。代わりに、丸められます(0x2000000
デフォルトの丸めモードに)。
正しい値のために無限に多くの場所が必要になるため、多くの結果が丸められるという事実。これには、1/3(無限に多くの場所をとる10進数でよく知られている)などの合理的な結果と、1/10(2の累乗ではないため、バイナリで無限に多くの場所をとる)の両方が含まれます。完璧な正方形ではないものの平方根のような不合理な結果も同様です。
二重丸め。一部のシステム(特にx86)では、浮動小数点式は公称型よりも高い精度で評価されます。つまり、上記のタイプの丸めのいずれかが発生すると、2つの丸めステップが発生します。最初に結果をより精度の高い型に丸め、次に最終型に丸めます。例として、1.49を整数(1)に丸めると10進数で何が起こるか、最初に小数第1位(1.5)に丸め、次にその結果を整数(2)に丸めるとどうなるかを考えます。コンパイラーの動作(特にバグの多い、GCCなどの非準拠コンパイラー)は予測できないため、これは実際には浮動小数点で処理するのが最も厄介な領域の1つです。
超越関数(trig
、exp
、log
、など)が正しく丸められた結果を持つように指定されていません。結果は、最後の精度の場所(通常1ulpと呼ばれる)の1単位内で正確であると指定されているだけです。
浮動小数点コードを作成するときは、結果が不正確になる可能性のある数値をどのように処理しているかに留意し、それに応じて比較を行う必要があります。多くの場合、「イプシロン」と比較することは理にかなっていますが、そのイプシロンは、絶対定数ではなく、比較する数値の大きさに基づいている必要があります。(絶対定数イプシロンが機能する場合、浮動小数点ではなく固定小数点がその仕事に適切なツールであることを強く示しています!)
編集:特に、マグニチュード相対イプシロンチェックは次のようになります。
if (fabs(x-y) < K * FLT_EPSILON * fabs(x+y))
はどこFLT_EPSILON
からの定数であるかfloat.h
(DBL_EPSILON
for double
sまたはLDBL_EPSILON
for long double
sに置き換えK
ます)、計算の累積エラーがK
最後の場所の単位によって確実に制限されるように選択した定数です(そして、エラーが発生したかどうかわからない場合バインドされた計算が正しく、計算で想定されている値K
よりも数倍大きくします)。
最後に、これを使用する場合FLT_EPSILON
、非正規化には意味がないため、ゼロ付近で特別な注意が必要になる場合があることに注意してください。簡単な修正はそれを作ることです:
if (fabs(x-y) < K * FLT_EPSILON * fabs(x+y) || fabs(x-y) < FLT_MIN)
DBL_MIN
ダブルを使用する場合も同様に置き換えます。
fabs(x+y)
x
とy
(異なる)符号が異なる場合は問題があります。それでも、カーゴカルト比較の流れに対する良い答えです。