web-dev-qa-db-ja.com

BigDecimalにゼロを掛ける

BigDecimalを使用して単純な乗算を実行していますが、ゼロを乗算すると奇妙な動作が見られます(このユースケースではゼロを乗算するのが正しいです)。

基本的な数学では、ゼロを掛けたものはすべてゼロに等しくなることがわかります( ゼロ積プロパティ および 乗算プロパティ を参照)

ただし、次のコードは常に同じエラーで失敗します。

assertEquals(new BigDecimal(0), new BigDecimal(22.3).multiply(new BigDecimal(0)));
Java.lang.AssertionError: 
Expected :0
Actual   :0E-48

これはBigDecimalの不正確さですか、それとも私がどこかに欠けている数学のニッチな分野がありますか?

注:IntelliJ11で実行されているJDK1.6.0_27

58
Richard

このアサーションのように、equals()メソッドを使用してBigDecimalsを比較することはできません。これは、この等しい関数がスケールを比較するためです。スケールが異なる場合、数学的に同じ数であっても、equals()はfalseを返します。

ただし、compareTo()を使用して、次のことを実行できます。

@assyliasが指摘しているように、倍精度の問題を回避するには、new BigDecimal("22.3")コンストラクターも使用する必要があります。

_BigDecimal expected = BigDecimal.ZERO;
BigDecimal actual = new BigDecimal("22.3").multiply(BigDecimal.ZERO);
assertEquals(0, expected.compareTo(actual));
_

signum()と呼ばれるメソッドもあります。このメソッドは、負、0、および正の場合に-1、0、または1を返します。したがって、ゼロをテストすることもできます

_assertEquals(0, actual.signum());
_
84
Keppil

コードには2つの問題があります。

  • 他の回答でアドバイスされているように、BigDecimalをequalsではなくcompareToと比較する必要があります
  • ただし、倍精度の問題を回避するために、二重コンストラクターnew BigDecimal("22.3")の代わりに文字列コンストラクターnew BigDecimal(22.3)も使用する必要があります。

つまり、次のコード(compareToを正しく使用)は引き続きfalseを返します。

BigDecimal bd = new BigDecimal(0.1).multiply(new BigDecimal(10));
System.out.println(bd.compareTo(BigDecimal.ONE) == 0);

なぜなら0.1d * 10d != 1

47
assylias

equals() on BigDecimal 比較のためにBigDecimalの内部状態をチェックします

以下のコードを参照してください

_public boolean equals(Object x) {
    if (!(x instanceof BigDecimal))
        return false;
    BigDecimal xDec = (BigDecimal) x;
    if (x == this)
        return true;
    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.inflate().equals(xDec.inflate());
}
_

値を比較する場合は、 compareTo() を使用します。

コードをに変更します

_assertEquals(0 , new BigDecimal(0).compareTo(new BigDecimal(22.3).multiply(new BigDecimal(0)));
_

更新:

精度の正確さのためにBigDecimalのパラメーターとしてStringをとるコンストラクターを使用して、以下の関連リンクを確認してください


関連項目

20
Jigar Joshi