BigDecimal乘以零

时间:2012-08-14 09:21:16

标签: java bigdecimal numerical-analysis rounding-error

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

基础数学告诉我,任何乘以零的数字都将等于零(参见:Zero Product PropertyMultiplication Properties

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

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

3 个答案:

答案 0 :(得分:84)

您不能使用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()的方法,它为负数,零和正数返回-1,0或1。所以你也可以用

测试零
assertEquals(0, actual.signum());

答案 1 :(得分: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

答案 2 :(得分:20)

BigDecimal上的

equals()检查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的参数,以便精确检查下面的相关链接


另见