58

我正在使用 BigDecimal 执行简单的乘法运算,并且在乘以零时发现了一些奇怪的行为(在此用例中乘以零是正确的)。

基本数学告诉我,任何乘以零都将等于零(请参阅:零乘积属性乘法属性

但是,以下代码将始终失败并出现相同的错误:

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

这是 BigDecimal 的不准确之处,还是我在某处遗漏了一些特定的数学分支?

注意:在 IntelliJ 11 中运行的 JDK 1.6.0_27

4

3 回答 3

84

你不能使用equals()方法来比较BigDecimals,就像这个断言一样。那是因为 this equals 函数会比较scale。如果比例不同,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(),它返回 -1、0 或 1 表示负数、零和正数。所以你也可以用

assertEquals(0, actual.signum());
于 2012-08-14T09:24:35.130 回答
47

您的代码有两个问题:

  • 您应该按照其他答案的建议将 BigDecimal 与 compareTo 而不是 equals 进行比较
  • 但你也应该使用字符串构造函数: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

于 2012-08-14T09:28:15.943 回答
20

equals()onBigDecimal检查内部状态以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)));

更新:

使用构造函数将 String 作为 BigDecimal 的参数,以确保精度检查下面的相关链接


另见

于 2012-08-14T09:25:50.030 回答