Effective Java by Joshua BlochとItem 8:オーバーライドするときは一般契約に従う)を読んでいます。このステートメントは書かれています
フロートフィールドの場合は、Float.compareメソッドを使用します。ダブルフィールドの場合は、Double.compareを使用します。 floatおよびdoubleフィールドの特別な処理は、Float.NaN、-0.0fおよび類似のdouble定数の存在により必要になります。
==
をfloatまたはdoubleの比較に使用できない理由の例を誰かに説明してもらえますか
Apidocから Float.compare
:
指定された2つのfloat値を比較します。返される整数値の符号は、呼び出しによって返される整数の符号と同じです。
new Float(f1).compareTo(new Float(f2))
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)
}
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は、負のゼロを正のゼロよりも厳密に小さいものとして扱うことに注意してください。
オペランドが正のゼロと負のゼロである厳密な比較の場合、結果は間違っています。
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つのペアです。
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
にも同じことが当てはまります。
浮動小数点オブジェクトを比較する理由は2つあります。
==
演算子は、数学的な比較を提供します。 NaN == NaN
に対してfalseを返し、-0.f == +0.f
に対してtrueを返します。
compare
およびcompareTo
ルーチンは、オブジェクトの比較を提供します。 NaNをそれ自体と比較するとき、それらは同じであることを示します(ゼロを返すことにより)。 -0.f
と+0.f
を比較するとき、それらは異なることを示します(ゼロ以外を返すことにより)。