BigDecimal精度と等しい比較ピット

3536 ワード

まず考えてみます。BigDecimalオブジェクトを作る時はどうやって作成しますか?
  • new一つ、マルチドロップ
  • BigDecimal.valueOfメソッドは、
  • の値を移動します。
     
    数字の種類としては、サイズを比較する操作がよくあります。等しいかどうかを比較する場合があります。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としてはかなり頼りないです。これからはまだ少ないです。
     
    もう一つ注意したいのは、コードを書いて他の人の書いた方法を調整する時は、ポイントを入れて見て実現したほうがいいです。いくら小さくても、よく使う方法では、穴を埋めることができます。