BigDecimal地板舍入出错

时间:2012-05-15 14:50:57

标签: java bigdecimal rounding

我最近问了一个关于奇怪的java双层舍入的问题,并得到了使用BigDecimals的答案,所以尝试了以下代码:

BigDecimal velocity = new BigDecimal(-0.07);
BigDecimal afterMultiplyingBy200 = velocity.multiply( new BigDecimal(200.0) );
BigDecimal floored = afterMultiplyingBy200.setScale(0, RoundingMode.FLOOR);
System.out.println("After multiplication " + afterMultiplyingBy200);
System.out.println("floored value is " + floored);

我得到了以下结果

After multiplication -14.000000000000001332267629550187848508358001708984375000
floored value is -15

似乎即使使用BigDecimal我也无法获得正确的值 - 将-0.07乘以200,我能做些什么来准确得到-14.0?

5 个答案:

答案 0 :(得分:6)

从我对这个问题的回答:

  

编译或解释代码时,您的“0.1”已经存在   四舍五入到该格式的最接近的数字,这导致一个小的   甚至在计算发生之前就会出现舍入错误。

问题是new BigDecimal(-0.07);使用double字面值来初始化BigDecimal - 因此错误仍然存​​在。使用BigDecimal代替String的构造函数。

答案 1 :(得分:3)

问题就在这里:

BigDecimal velocity = new BigDecimal(-0.07);

-0.07无法完全表示为double,因此传递给BigDecimal构造函数的文字值最终与-0.07略有不同。 BigDecimal只需获取该近似值并与其一起运行,从而产生您所看到的结果。

尝试:

BigDecimal velocity = new BigDecimal(-7, 2);
BigDecimal afterMultiplyingBy200 = velocity.multiply( new BigDecimal(2, -2) );

答案 2 :(得分:3)

您应该使用string constructor来避免因使用双打而导致的舍入错误:

BigDecimal velocity = new BigDecimal("-0.07");
BigDecimal afterMultiplyingBy200 = velocity.multiply(new BigDecimal("200"));

constructor using doubles的Javadoc摘录 - 强调我的:

  
      
  1. 此构造函数的结果可能有些不可预测。有人可能会假设在Java中编写新的BigDecimal(0.1)会创建一个BigDecimal,它恰好等于0.1(未缩放值为1,标度为1),但它实际上等于0.1000000000000000055511151231257827021181583404541015625。这是因为0.1不能精确地表示为double(或者,就此而言,作为任何有限长度的二进制分数)。因此,传递给构造函数的值并不完全等于0.1,尽管有外观。
  2.   
  3. 另一方面,String构造函数是完全可预测的:写入新的BigDecimal(“0.1”)会创建一个BigDecimal,它正好等于0.1,正如人们所期望的那样。因此,通常建议使用String构造函数优先于此构造函数。
  4.   

答案 3 :(得分:0)

您应该查看RoundingMode,尤其是HALF_UP

答案 4 :(得分:0)

根据the documentationROUND_FLOOR向负无穷大舍入,从而将-14.000...四舍五入为-15