web-dev-qa-db-ja.com

「==」を使用して2つのfloatまたはdoubleの数値を比較できないのはなぜですか

Effective Java by Joshua BlochとItem 8:オーバーライドするときは一般契約に従う)を読んでいます。このステートメントは書かれています

フロートフィールドの場合は、Float.compareメソッドを使用します。ダブルフィールドの場合は、Double.compareを使用します。 floatおよびdoubleフィールドの特別な処理は、Float.NaN、-0.0fおよび類似のdouble定数の存在により必要になります。

==をfloatまたはdoubleの比較に使用できない理由の例を誰かに説明してもらえますか

26
Anand

Apidocから Float.compare

指定された2つのfloat値を比較します。返される整数値の符号は、呼び出しによって返される整数の符号と同じです。

new Float(f1).compareTo(new Float(f2))

Float.compareTo

2つのFloatオブジェクトを数値的に比較します。このメソッドによって実行される比較は、プリミティブな浮動小数点値に適用される場合、Java言語の数値比較演算子(<、<=、==、> =>)によって実行されるものとは異なる2つの方法があります。 :

  • このメソッドでは、Float.NaNはそれ自体と等しく、他のすべてのfloat値(Float.POSITIVE_INFINITYを含む)よりも大きいと見なされます。
  • このメソッドでは、0.0fは-0.0fより大きいと見なされます。

これにより、このメソッドによって課されるFloatオブジェクトの自然な順序は、equalsと一致します。

次のコードを検討してください。

    System.out.println(-0.0f == 0.0f); //true
    System.out.println(Float.compare(-0.0f, 0.0f) == 0 ? true : false); //false      
    System.out.println(Float.NaN == Float.NaN);//false
    System.out.println(Float.compare(Float.NaN, Float.NaN) == 0 ? true : false); //true
    System.out.println(-0.0d == 0.0d); //true
    System.out.println(Double.compare(-0.0d, 0.0d) == 0 ? true : false);//false     
    System.out.println(Double.NaN == Double.NaN);//false
    System.out.println(Double.compare(Double.NaN, Double.NaN) == 0 ? true : false);//true        

数値ではないものは単に数値ではなく、数値比較の観点からは等しいものとして扱う必要があるため、出力は正しくありません。 0=-0であることも明らかです。

Float.compare の機能を見てみましょう。

public static int compare(float f1, float f2) {
   if (f1 < f2)
        return -1;           // Neither val is NaN, thisVal is smaller
    if (f1 > f2)
        return 1;            // Neither val is NaN, thisVal is larger

    int thisBits = Float.floatToIntBits(f1);
    int anotherBits = Float.floatToIntBits(f2);

    return (thisBits == anotherBits ?  0 : // Values are equal
            (thisBits < anotherBits ? -1 : // (-0.0, 0.0) or (!NaN, NaN)
             1));                          // (0.0, -0.0) or (NaN, !NaN)
}

Float.floatToIntBits

IEEE 754浮動小数点「単一フォーマット」ビットレイアウトに従って、指定された浮動小数点値の表現を返します。ビット31(マスク0x80000000で選択)は、浮動小数点数の符号を表します。ビット30〜23(マスク0x7f800000で選択されるビット)は指数を表します。ビット22〜0(マスク0x007fffffによって選択されるビット)は、浮動小数点数の仮数(仮数と呼ばれることもあります)を表します。

引数が正の無限大の場合、結果は0x7f800000です。

引数が負の無限大の場合、結果は0xff800000です。

引数がNaNの場合、結果は0x7fc00000です。

すべての場合において、結果は整数であり、intBitsToFloat(int)メソッドに渡されると、floatToIntBitsへの引数と同じ浮動小数点値を生成します(単一の「標準」NaN値に折りたたまれました)。

JLS 15.20.1。数値比較演算子<、<=、>、および> =

IEEE 754標準の仕様によって決定される浮動小数点比較の結果は次のとおりです。

  • いずれかのオペランドがNaNの場合、結果はfalseです。

  • NaN以外のすべての値は、すべての有限値よりも小さい負の無限大、およびすべての有限値よりも大きい正の無限大で順序付けられています。

  • 正のゼロと負のゼロは等しいと見なされます。たとえば、-0.0 <0.0はfalseですが、-0.0 <= 0.0はtrueです。

  • ただし、メソッドMath.minおよびMath.maxは、負のゼロを正のゼロよりも厳密に小さいものとして扱うことに注意してください。

オペランドが正のゼロと負のゼロである厳密な比較の場合、結果は間違っています。

JLS 15.21.1。数値等価演算子==および!=

IEEE 754標準の仕様によって決定される浮動小数点比較の結果は次のとおりです。

浮動小数点の等価性テストは、IEEE 754標準の規則に従って実行されます。

  • いずれかのオペランドがNaNの場合、==の結果はfalseですが、!=の結果はtrueです。実際、テストx!= xは、xの値がNaNの場合にのみ真です。メソッドFloat.isNaNおよびDouble.isNaNを使用して、値がNaNかどうかをテストすることもできます。

  • 正のゼロと負のゼロは等しいと見なされます。たとえば、-0.0 == 0.0はtrueです。

  • それ以外の場合、2つの異なる浮動小数点値は、等価演算子によって等しくないと見なされます。特に、正の無限大を表す1つの値と負の無限大を表す1つの値があります。それぞれは、それ自体とのみ等しく比較され、それぞれは他のすべての値と等しくないものと比較されます。

両方のオペランドが NaN である等値比較の場合、結果は間違っています。

合計順序(=<><=>= は多くの重要なアルゴリズムで使用されているため(- Comparableインターフェースを実装するすべてのクラス )より一貫した動作が得られるため、比較メソッドを使用することをお勧めします。

IEEE-754標準のコンテキストでの合計順序 の結果は、正のゼロと負のゼロの差です。

たとえば、compareメソッドの代わりに等値演算子を使用し、値のコレクションがあり、コードロジックが要素の順序に基づいていくつかの決定を行い、何らかの方法ですべてのNaN値を取得し始める場合代わりに同じ値として異なる値として扱われます。

NaN値の量/割合に比例して、プログラムの動作にエラーが発生する可能性があります。また、正と負のゼロが多数ある場合、エラーでロジックに影響を与えるのはたった1つのペアです。

フロート ses IEEE-754 32ビット形式およびDouble ses IEEE-754 64ビット形式。

18
linski

float(およびdouble)には、「数字」ではない特別な意味のために予約されている特別なビットシーケンスがあります。

  • 負の無限大、内部表現_0xff800000_
  • 正の無限大、内部表現_0x7f800000_
  • 数値ではなく、内部表現_0x7fc00000_

これらはそれぞれ、Float.compare()を使用してそれ自体と比較すると_0_(「同じ」という意味)を返しますが、_==_を使用した以下の比較は_Float.NaN_の場合と異なります:

_Float.NEGATIVE_INFINITY == Float.NEGATIVE_INFINITY // true
Float.POSITIVE_INFINITY == Float.POSITIVE_INFINITY // true
Float.NaN == Float.NaN // false
_

したがって、float値を比較する場合、特別な_Float.NaN_値を含むすべての値で一貫性を保つために、Float.compare()が最適なオプションです。

doubleにも同じことが当てはまります。

5
Bohemian

浮動小数点オブジェクトを比較する理由は2つあります。

  • 私は数学をやっているので、数値を比較したいと思います。数値的に、–0は+0に等しく、NaNはそれ自体でなく、何にも等しくありません。「等しい」は数字のみが持つプロパティであり、NaNは数字ではないためです。
  • 私はコンピューターでオブジェクトを操作しているので、異なるオブジェクトを区別して順番に配置する必要があります。これは、たとえばツリーまたは他のコンテナ内のオブジェクトをソートするために必要です。

==演算子は、数学的な比較を提供します。 NaN == NaNに対してfalseを返し、-0.f == +0.fに対してtrueを返します。

compareおよびcompareToルーチンは、オブジェクトの比較を提供します。 NaNをそれ自体と比較するとき、それらは同じであることを示します(ゼロを返すことにより)。 -0.f+0.fを比較するとき、それらは異なることを示します(ゼロ以外を返すことにより)。

2