浮動小数点値が0に等しいかどうかをチェックしても安全ですか?


100

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

0.00000000000001と0.00000000000002の間の不正確さは理解できますが、0は何もないため、混乱するのはかなり難しいようです。何も不正確だとしたら、それはもはや何もありません。

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

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

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


69
三項演算子はそのコードでは冗長です:)
Joel Coehoorn 2009年

5
笑笑 Go me
ジーンロバーツ、

xがゼロに設定された方法がわからないので、私はそれをしません。それでもやりたい場合は、おそらくxを丸めるか、xをフロアして、最後にタグ付けされている可能性がある1e-12などを削除します。
レックスローガン

回答:


115

それは安全truedouble変数の値が正確に0.0(元のコードスニペットではもちろんそうです)である場合にのみ、比較が返されることを期待しです。これは、==オペレーターのセマンティクスと一致しています。a == baが等しいbます。

それは安全ではないことがあるので(正しくありません)いくつかの計算の結果は、純粋数学同じ演算結果がゼロであるときはいつでも二重(またはより一般的には、浮動小数点)算術演算でゼロになることを期待します。これは、計算が実行されると、浮動小数点の精度エラーが発生するためです。これは、数学の実数算術には存在しない概念です。


51

多数の「同等」比較を行う必要がある場合は、比較のために.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);

4
これらの数値が互いに非常に近い値を持っている場合、double1とdouble2を比較することにより、減算によるキャンセルエラーが発生する可能性があります。私はMath.Abs​​を削除し、各ブランチを個別にチェックしますd1> = d2-eおよびd1 <= d2 + e
Theodore Zographos

「イプシロンはゼロに近い正の値の最小式を定義するため、2つの類似した値の差のマージンはイプシロンより大きくなければなりません。通常、イプシロンよりも何倍も大きくなります。そのため、等しいかどうかDouble値を比較するときにイプシロンを使用しないでください。」- msdn.microsoft.com/en-gb/library/ya2zha7s(v=vs.110).aspx
ラファエル・コスタ

15

単純なサンプルの場合、そのテストは問題ありません。しかし、これはどうですか:

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 );

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


5
実際、これは何らかの理由でtrueを返します(少なくともLINQPadでは)。
Alexey Romanov

あなたが話している「.1号」とは何ですか?
Teejay

14

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

比較の精度

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

...

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

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


1
また、同等ではない値を比較することもできます。ifの場合は、の場合はと予想されますがx.Equals(y)、is とisの(1/x).Equals(1/y)場合xはそうでは0ありません。これらの値は、逆数ではなくても等しいと宣言します。y1/Double.NegativeInfinity
スーパーキャット2012

@supercat:それらは同等です。そして、彼らには相反関係はありません。あなたは再びあなたのテストを実行することができx = 0そしてy = 0、あなたはまだそれを見つけるだろう1/x != 1/y
Ben Voigt 2016

@BenVoigt:付きxyタイプなどdouble?どのように結果を比較して、等しくないレポートを作成しますか?1 / 0.0はNaNではないことに注意してください。
スーパーキャット2016

@supercat:OK、それはIEEE-754が間違っていることの1つです。(1 1.0/0.0つ目は、制限が一意ではないため、本来のNaNにならないことです。2つ目は、無限大は、無限大に注意を払わずに互いに等しいことを示します)
Ben Voigt

@BenVoigt:ゼロが2つの非常に小さい数の乗算の結果である場合、1.0をそれに割ると、同じ数の小さい数のいずれかよりも大きい値で比較され、小さい方のいずれかである場合は任意の数よりも小さい値が得られます。数は反対の符号を持っていました。私見、IEEE-754は、符号なしのゼロがあり、正と負の無限小がある場合に優れています。
スーパーキャット2016

6

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

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

問題は、プログラマーが比較のために暗黙の型キャスト(doubleからfloatへ)が起こっていることを忘れることがあり、その結果、バグが発生することです。


3

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

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

整数を割り当てることから始めることもでき、整数による加算、減算、または乗算に固執することにより、単純な比較が引き続き機能します(floatの場合は24ビット未満、doubleの場合は53ビットであると想定)。したがって、特定の制御された条件下では、floatとdoubleを整数として扱うことができます。


私は一般的にあなたの声明に同意します(そしてそれを支持しました)が、IEEE 754浮動小数点実装が使用されているかどうかは本当に依存していると思います。そして、私はすべての「現代の」コンピュータが少なくともフロートの格納のためにIEEE 754を使用していると信じています(異なる奇妙な丸め規則があります)。
Mark Lakata 2017年

2

いいえ、大丈夫ではありません。いわゆる非正規化値(サブノーマル)を0.0と比較すると、偽(ゼロ以外)として比較されますが、方程式で使用すると正規化されます(0.0になります)。したがって、これをゼロ除算を回避するメカニズムとして使用することは安全ではありません。代わりに、1.0を追加して1.0と比較してください。これにより、すべてのサブノーマルが確実にゼロとして扱われます。


サブノーマルはデノーマルと
マヌエル

サブノーマルは、実際の操作によっては同じ結果が得られる場合と得られない場合がありますが、使用してもゼロにはなりません。
wnoise 2011年

-2

これを試してみると、==がdouble / floatに対して信頼できないことがわかります。
double d = 0.1 + 0.2; bool b = d == 0.3;

これがQuoraからの回答です。


-4

実際には、以下のコードを使用してdouble値を0.0と比較する方が良いと思います。

double x = 0.0;
return (Math.Abs(x) < double.Epsilon) ? true : false;

フロートについても同じです:

float x = 0.0f;
return (Math.Abs(x) < float.Epsilon) ? true : false;

5
いいえ。double.Epsilonのドキュメントから:「2つの浮動小数点数が等しいと見なすことができるかどうかを判断するカスタムアルゴリズムを作成する場合は、イプシロン定数より大きい値を使用して、許容可能な差の絶対マージンを確立する必要があります。 2つの値が等しいと見なされるため(通常、その差のマージンはEpsilonよりも何倍も大きい)。 "
Alastair Maw

1
@AlastairMawこれは、任意のサイズの2つのdoubleが等しいかどうかをチェックする場合に適用されます。ゼロに等しいかどうかを確認するには、double.Epsilonで問題ありません。
jwg 2013年

4
いいえ、そうではありませ。ある計算によって到達した値がゼロから数倍イプシロン離れている可能性は非常に高いですが、それでもゼロと見なす必要があります。たまたまゼロに近いからといって、どこかからの中間結果で魔法のように余分な精度をすべて達成することはありません。
Alastair Maw 2013年

4
例:(1.0 / 5.0 + 1.0 / 5.0-1.0 / 10.0-1.0 / 10.0-1.0 / 10.0-1.0 / 10.0)<double.Epsilon == false(そして、マグニチュードではかなりそう:2.78E-17 vs 4.94E -324)
Alastair Maw

したがって、double.Epsilonが適切でない場合、推奨される精度はどのくらいですか?イプシロンの10回は大丈夫でしょうか?100回?
リャン2013年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.