web-dev-qa-db-ja.com

0と等しいかどうかの浮動小数点値をチェックするのは安全ですか?

通常、double型またはdecimal型の値の等値に依存することはできないことは知っていますが、0が特別な場合かどうか疑問に思っています。

0.00000000000001と0.00000000000002の間の不正確さは理解できますが、0自体は無意味なので混乱するのはかなり難しいようです。何も不正確な場合は、もう何もありません。

しかし、私はこのトピックについてあまり知りませんので、言うことはありません。

double x = 0.0;
return (x == 0.0) ? true : false;

それは常に真を返しますか?

93
Gene Roberts

safeは、double変数の値が正確に0.0(元のコードスニペットではもちろん)である場合にのみ、比較がtrueを返すことを期待することです。 、 ケース)。これは、==演算子のセマンティクスと一致しています。 a == bは、「abと等しい」ことを意味します。

安全ではない正しくないであるため)計算の結果が二重(またはより一般的には浮動小数点)演算でゼロになると予想されるのは、結果が純粋な数学の同じ計算のゼロです。これは、計算が実行されると、浮動小数点精度エラーが表示されるためです。これは、数学の実数演算には存在しない概念です。

109
Daniel Daranas

多くの「等価」比較を行う必要がある場合は、比較のために.NET 3.5で小さなヘルパー関数または拡張メソッドを作成することをお勧めします。

public static bool AlmostEquals(this double double1, double double2, double precision)
{
    return (Math.Abs(double1 - double2) <= precision);
}

これは次の方法で使用できます。

double d1 = 10.0 * .1;
bool equals = d1.AlmostEquals(0.0, 0.0000001);
49
Dirk Vollmar

簡単なサンプルの場合、そのテストは大丈夫です。しかし、これはどうですか:

bool b = ( 10.0 * .1 - 1.0 == 0.0 );

.1は2進数で繰り返される10進数であり、正確に表現できないことに注意してください。次に、このコードと比較してください:

double d1 = 10.0 * .1; // make sure the compiler hasn't optimized the .1 issue away
bool b = ( d1 - 1.0 == 0.0 );

実際の結果を確認するためにテストを実行します。そのように覚えている可能性が高くなります。

15
Joel Coehoorn

Double.Equals のMSDNエントリから:

比較の精度

Equalsメソッドは注意して使用する必要があります。2つの値の精度が異なるため、2つの明らかに等価な値が等しくない可能性があるためです。次の例では、Doubleの値.3333と1を3で除算して返されるDoubleが等しくないことを報告しています。

...

同等性を比較するのではなく、推奨される手法の1つでは、2つの値の差の許容マージン(値の1つの.01%など)を定義します。 2つの値の差の絶対値がそのマージン以下である場合、差は精度の違いによる可能性が高いため、値は等しい可能性があります。次の例では、この手法を使用して、前のコード例で等しくないことが判明した2つのDouble値である.33333と1/3を比較します。

また、 Double.Epsilon を参照してください。

13
Stu Mackellar

問題は、さまざまなタイプの浮動小数点値の実装を比較するときに発生します。 floatとdoubleを比較します。しかし、同じタイプであれば、問題になることはありません。

float f = 0.1F;
bool b1 = (f == 0.1); //returns false
bool b2 = (f == 0.1F); //returns true

問題は、プログラマーが比較のために暗黙の型キャスト(doubleからfloat)が行われていることを忘れてしまい、バグになることです。

6
Yogee

数値が直接floatまたはdoubleに割り当てられた場合、ゼロまたはdoubleの場合は53ビットで、floatの場合は24ビットで表現できる整数に対してテストしても安全です。

または、別の方法で言えば、常に整数値をdoubleに割り当ててから、そのdoubleを同じ整数と比較して、等しいことを保証できます。

また、整数を割り当てることから始めて、整数による加算、減算、または乗算に固執することにより、単純な比較を引き続き機能させることができます(結果は浮動小数点数で24ビット未満、倍精度で53ビットであると仮定)。そのため、特定の制御された条件下で、floatとdoubleを整数として扱うことができます。

3
Kevin Gale

いいえ、大丈夫ではありません。いわゆる非正規化値(非正規)は、0.0と比較すると偽(非ゼロ)として比較されますが、式で使用されると正規化されます(0.0になります)。したがって、これをゼロ除算を回避するメカニズムとして使用することは安全ではありません。代わりに、1.0を追加して1.0と比較します。これにより、すべての非正規分布がゼロとして扱われるようになります。

2
Bob job