BigDecimal精度と等しい比較ピット
3536 ワード
まず考えてみます。BigDecimalオブジェクトを作る時はどうやって作成しますか? new一つ、マルチドロップ BigDecimal.valueOfメソッドは、 の値を移動します。
数字の種類としては、サイズを比較する操作がよくあります。等しいかどうかを比較する場合があります。equal方法ですか?それともcompreTo方法ですか?ここは大きな穴です。
上記のいくつかのfalseに戻るcaseは、精度が違うからです。精度が違う原因は、BigDecimalオブジェクトの初期化方式が違っています。ソースから見ると、前の3つの初期化方式が違います。
したがって、BigDecimalはサイズが大きいですか?それともcompreToの方法でスペクトルを比較してcompreToに変更した後、上の4つのcaseが返ってきた結果はすべて等しいです。
考えてみますと、小数のタイプはコンピュータの中で元々正確に保存できません。HashMapのkeyとしてはかなり頼りないです。これからはまだ少ないです。
もう一つ注意したいのは、コードを書いて他の人の書いた方法を調整する時は、ポイントを入れて見て実現したほうがいいです。いくら小さくても、よく使う方法では、穴を埋めることができます。
数字の種類としては、サイズを比較する操作がよくあります。等しいかどうかを比較する場合があります。equal方法ですか?それともcompreTo方法ですか?ここは大きな穴です。
//new double
BigDecimal newZero = new BigDecimal(0.0);
System.out.println(BigDecimal.ZERO.equals(newZero));
//new
BigDecimal stringNewZero = new BigDecimal("0.0");
System.out.println(BigDecimal.ZERO.equals(stringNewZero));
//valueOf double
BigDecimal noScaleZero = BigDecimal.valueOf(0.0);
System.out.println(BigDecimal.ZERO.equals(noScaleZero));
//valueOf double, 1
BigDecimal scaleZero = BigDecimal.valueOf(0.0).setScale(1);
System.out.println(BigDecimal.ZERO.equals(scaleZero));
比較に使う値は全部0です。上記のequals方法で返した結果は何ですか?全部trueですか?ノノノノ…true
false
false
false
びっくりしても驚きません。意外ですか?原因は何ですか?BigDecimalのequals方法の実現を見てください。public boolean equals(Object x) {
// , false
if (!(x instanceof BigDecimal))
return false;
BigDecimal xDec = (BigDecimal) x;
// , true
if (x == this)
return true;
// , false!!
if (scale != xDec.scale)
return false;
long s = this.intCompact;
long xs = xDec.intCompact;
if (s != INFLATED) {
if (xs == INFLATED)
xs = compactValFor(xDec.intVal);
return xs == s;
} else if (xs != INFLATED)
return xs == compactValFor(this.intVal);
return this.inflated().equals(xDec.inflated());
}
前の3つの簡単な判断から分かります。debugは上のequals方法と比べて3つの戻りfalseがあります。精度が違います。BigDecimal.ZEROの精度はどれぐらいですか?ソースコードを見てください:// Cache of common small BigDecimal values.
private static final BigDecimal zeroThroughTen[] = {
new BigDecimal(BigInteger.ZERO, 0, 0, 1),
new BigDecimal(BigInteger.ONE, 1, 0, 1),
new BigDecimal(BigInteger.valueOf(2), 2, 0, 1),
new BigDecimal(BigInteger.valueOf(3), 3, 0, 1),
new BigDecimal(BigInteger.valueOf(4), 4, 0, 1),
new BigDecimal(BigInteger.valueOf(5), 5, 0, 1),
new BigDecimal(BigInteger.valueOf(6), 6, 0, 1),
new BigDecimal(BigInteger.valueOf(7), 7, 0, 1),
new BigDecimal(BigInteger.valueOf(8), 8, 0, 1),
new BigDecimal(BigInteger.valueOf(9), 9, 0, 1),
new BigDecimal(BigInteger.TEN, 10, 0, 2),
};
/**
* The value 0, with a scale of 0.
*
* @since 1.5
*/
public static final BigDecimal ZERO = zeroThroughTen[0];
Big Decimal.ZERO値は0で、精度は0.上記のいくつかのfalseに戻るcaseは、精度が違うからです。精度が違う原因は、BigDecimalオブジェクトの初期化方式が違っています。ソースから見ると、前の3つの初期化方式が違います。
したがって、BigDecimalはサイズが大きいですか?それともcompreToの方法でスペクトルを比較してcompreToに変更した後、上の4つのcaseが返ってきた結果はすべて等しいです。
BigDecimal newZero = new BigDecimal(0.0);
System.out.println(BigDecimal.ZERO.compareTo(newZero));
BigDecimal stringNewZero = new BigDecimal("0.0");
System.out.println(BigDecimal.ZERO.compareTo(stringNewZero));
BigDecimal noScaleZero = BigDecimal.valueOf(0.0);
System.out.println(BigDecimal.ZERO.compareTo(noScaleZero));
BigDecimal scaleZero = BigDecimal.valueOf(0.0).setScale(1);
System.out.println(BigDecimal.ZERO.compareTo(scaleZero));
出力結果0
0
0
0
これによって連想されるより大きなピットは、BigDecimalの値をHashMapのkeyとすると、精度の問題で同じ値でhashCodeの値が異なる可能性があり、equals方法でfalseに戻るとputとgetは同じ値を持つ可能性が高いが、異なるvalueにアクセスした。考えてみますと、小数のタイプはコンピュータの中で元々正確に保存できません。HashMapのkeyとしてはかなり頼りないです。これからはまだ少ないです。
もう一つ注意したいのは、コードを書いて他の人の書いた方法を調整する時は、ポイントを入れて見て実現したほうがいいです。いくら小さくても、よく使う方法では、穴を埋めることができます。