我最近问了一个关于奇怪的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?
答案 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摘录 - 强调我的:
- 此构造函数的结果可能有些不可预测。有人可能会假设在Java中编写新的BigDecimal(0.1)会创建一个BigDecimal,它恰好等于0.1(未缩放值为1,标度为1),但它实际上等于0.1000000000000000055511151231257827021181583404541015625。这是因为0.1不能精确地表示为double(或者,就此而言,作为任何有限长度的二进制分数)。因此,传递给构造函数的值并不完全等于0.1,尽管有外观。
- 另一方面,String构造函数是完全可预测的:写入新的BigDecimal(“0.1”)会创建一个BigDecimal,它正好等于0.1,正如人们所期望的那样。因此,通常建议使用String构造函数优先于此构造函数。
醇>
答案 3 :(得分:0)
您应该查看RoundingMode,尤其是HALF_UP
。
答案 4 :(得分:0)
根据the documentation,ROUND_FLOOR
向负无穷大舍入,从而将-14.000...
四舍五入为-15
。