web-dev-qa-db-ja.com

C#でのdouble値の比較

doubleというx変数があります。コードでは、x0.1の値が割り当てられ、x0.1を比較する 'if'ステートメントでチェックします

if (x==0.1)
{
----
}

残念ながら、ifステートメントは入力しません

  1. Doubleまたはdoubleを使用する必要がありますか?

  2. この背後にある理由は何ですか?これに対する解決策を提案できますか?

これは、コンピューターが浮動小数点値をどのように保存するかによる標準的な問題です。ここで「浮動小数点の問題」を検索すると、膨大な情報が見つかります。

つまり、float/doubleは0.1を正確に格納できません。常に少しずれます。

数値を10進表記で保存するdecimalタイプを使用してみてください。したがって、0.1は正確に表現できます。


あなたは理由を知りたいと思った:

Float/doubleは、小数ではなくバイナリの小数として保存されます。説明する:

12.34 10進表記(使用するもの)は

1 * 101 + 2 * 10 + 3 * 10-1 + 4 * 10-2

コンピューターは、ベース2を使用することを除いて、同じ方法で浮動小数点数を格納します:10.01

1 * 21 + 0 * 2 + 0 * 2-1 + 1 * 2-2

これで、おそらく、10進表記では完全に表現できない数値があることをご存じでしょう。たとえば、10進表記の1/30.3333333…です。バイナリ表記でも同じことが起こりますが、正確に表現できない数値が異なる点が異なります。その中には、1/10という番号があります。バイナリ表記では0.000110011001100…です。

バイナリ表記では正確に格納できないため、丸められた方法で格納されます。したがって、あなたの問題。

90
Vilx-

doubleDoubleは同じであり(doubleDoubleのエイリアスです)、同じ意味で使用できます。

Doubleを別の値と比較する際の問題は、doubleは近似値であり、正確な値ではないことです。したがって、x0.1に設定すると、実際には0.100000001またはそのようなものとして保存されます。

同等性をチェックする代わりに、定義された最小差(許容値)よりも小さいことを確認する必要があります。何かのようなもの:

if (Math.Abs(x - 0.1) < 0.0000001)
{
    ...
}
39
Rune Grimstad

比較する_Math.Abs_上の_X-Y_とvalueの組み合わせが必要です。

次の拡張方法アプローチを使用できます

_public static class DoubleExtensions
    {
        const double _3 = 0.001;
        const double _4 = 0.0001;
        const double _5 = 0.00001;
        const double _6 = 0.000001;
        const double _7 = 0.0000001;

        public static bool Equals3DigitPrecision(this double left, double right)
        {
            return Math.Abs(left - right) < _3;
        }

        public static bool Equals4DigitPrecision(this double left, double right)
        {
            return Math.Abs(left - right) < _4;
        }

        ...
_

ToString以外のdoubleでメソッドを呼び出すことはめったにないので、かなり安全な拡張だと思います。

次に、xyを比較できます

if(x.Equals4DigitPrecision(y))

19
Valentin Kuzub

浮動小数点数の比較は、丸めのために常に正確に実行できるとは限りません。比べる

(x == .1)

コンピューターは本当に比較します

(x - .1) vs 0

Sybtractionの結果は、マシン上で浮動小数点数がどのように表されるかにより、必ずしも正確に再設定できるとは限りません。したがって、ゼロ以外の値を取得すると、条件はfalseに評価されます。

この比較を克服するために

Math.Abs(x- .1) vs some very small threshold ( like 1E-9)
10
sharptooth

ドキュメント から:

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

...

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

したがって、本当にダブルが必要な場合は、ドキュメントに記載されているテクニックを使用する必要があります。可能であれば、10進数に変更します。 遅くなりますが、この種の問題は発生しません。

5
Alfred Myers

Doubleとdoubleは同一です。

理由については、 http://www.yoda.arachsys.com/csharp/floatingpoint.html を参照してください。要するに、doubleは正確な型ではなく、「x」と「0.1」のわずかな違いがそれをスローします。

3
Hans Kesting

浮動小数点値の正確な比較は、丸めと内部表現の問題のために常に機能するとは限らないことがわかっています。

不正確な比較を試してください:

if (x >= 0.099 && x <= 0.101)
{
}

もう1つの方法は、decimalデータ型を使用することです。

3
user151323

decimalを使用します。この「問題」はありません。

2

1)ダブルまたはダブルを使用する必要がありますか?

Doubledoubleは同じものです。 doubleは、クラスのエイリアスとして機能する単なるC#キーワードですSystem.Double最も一般的なことは、エイリアスを使用することです! stringSystem.String)、intSystem.Int32

組み込み型テーブル(C#リファレンス) も参照してください。

1

Double(一部の言語ではfloatと呼ばれます)は丸めの問題による問題を抱えていますが、おおよその値が必要な場合にのみ有効です。

Decimalデータ型は、必要なことを行います。

参照の10進数と10進数は、.NET C#で同じであり、double型とDouble型も同じです。どちらも同じ型を参照します(ただし、decimalとdoubleは見たとおり非常に異なります)。

Decimalデータ型にはいくつかのコストが伴うことに注意してください。ループなどを見る場合は注意して使用してください。

1
Timothy Walters

Javaコードベースからヒントを取り、.CompareToとゼロ比較をテストします。これは、.CompareTo関数は、浮動小数点の等価性を正確な方法で考慮します。例えば、

System.Math.PI.CompareTo(System.Math.PI) == 0

この述部はtrueを返す必要があります。

1
Nicole Tedesco
// number of digits to be compared    
public int n = 12 
// n+1 because b/a tends to 1 with n leading digits
public double Epsilon { get; } = Math.Pow(10, -(n+1)); 

public bool IsEqual(double a, double b)
{
    if (Math.Abs(a)<= double.Epsilon || Math.Abs(b) <= double.Epsilon)
        return Math.Abs(a - b) <=  double.Epsilon;
    return Math.Abs(1.0 - a / b) <=  Epsilon;
}
0
Sorush

原則として:

ほとんどの場合、二重表現で十分ですが、状況によっては惨めに失敗する可能性があります。完全な精度が必要な場合は10進数値を使用します(金融アプリケーションなど)。

Doubleのほとんどの問題は直接比較に起因するものではなく、丸め誤差と小数誤差(特に乗算と除算)により値を指数関数的に乱すいくつかの数学演算の累積の結果として使用されます。

コードが以下の場合、ロジックを確認します。

x = 0.1

if (x == 0.1)

失敗してはいけません、失敗するのは簡単です、X値がより複雑な手段または操作によって計算された場合、デバッガで使用されるToStringメソッドはスマートな丸めを使用している可能性があります、おそらくあなたは同じことを行うことができます(それがあまりにも危険な場合10進数の使用に戻ります):

if (x.ToString() == "0.1")
0
Jorge Córdoba

私が見つけたクールなハックは、ダブルを表すintを返す.GetHashCode()メソッドを使用することです。

_(0.4d + 0.3d + 0.2d + 0.1d).GetHashCode() //returns -1072693248_

1d.GetHashCode() //returns 1072693248

あなたが今述べたように、私たちはこのようなものを使用することができます

_public static bool AccurateEquality(double first,double second)
{
    return Math.Abs(first.GetHashCode()) == Math.Abs(second.GetHashCode());
}
_

使用法:AccurateEquality((0.4d + 0.3d + 0.2d + 0.1d),1) //returns true

while:_(0.4d + 0.3d + 0.2d + 0.1d) == 1d //returns false_

複数のケースで試しましたが、うまくいくようです。

0
bigworld12

浮動小数点数の表現が不正確であることで有名です(フロートが内部に格納される方法のため)。 xは実際には0.0999999999または0.100000001であり、条件は失敗します。フロートが等しいかどうかを判断する場合は、フロートが特定の許容範囲内にあるかどうかを特定する必要があります。

つまり.

if(Math.Abs(x) - 0.1 < tol) {
   // Do something 
}
0
Massif