2012-08-14 241 views
58

我正在用BigDecimal执行一个简单的乘法,当乘以零(在这个用例中乘以零是正确的)时,我发现了一些奇怪的行为。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的一个不准确的地方,还是我在某处丢失了一些数学分支?

注:JDK 1.6.0_27中的IntelliJ 11

+3

是考虑数值分析,特别是逼近和截断误差 – 2012-08-14 09:26:31

+0

或者在'double'你可以写'assertEquals(0,23.3 * 0,0);';) – 2012-08-14 09:29:01

+11

并且查看'BigDecimal.ZERO'。 – EJP 2012-08-14 10:41:14

回答

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()); 
20

equals()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的精度精密检查以下


相关链接请参见

47

有2个问题,你的代码:

  • 你应该的compareTo,而不是平等比较的BigDecimal,通过其他的答案
  • 的建议,但你也应该使用字符串构造函数: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

+0

感谢您指出构造函数更改 – Richard 2012-08-14 09:40:00

+0

@assylias“,但您还应该使用字符串构造函数:new BigDecimal(”22.3“),而不是双重构造函数new BigDecimal(22.3)以避免双精度问题。你能解释双精度问题吗? – Geek 2012-08-16 07:49:16

+1

@Geek试试这个'System.out.println(新的BigDecimal(“0.1”));'和这个'System.out.println(新的BigDecimal(0.1d));'。第一个(String构造函数)打印'0.1',而第二个(双构造函数)打印'0.1000000000000000055511151231257827021181583404541015625'。发生这种情况是因为double不能准确地表示0.1。关于它的更多信息[例如在此页上](http:// stackoverflow。COM /问题/ 4937402 /移动-小数位,在功能于一个双)。 – assylias 2012-08-16 08:26:48