2

我最近问了一个关于奇怪的 java double floor rounding 的问题,并得到了使用 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?

4

5 回答 5

7

从我对那个问题的回答:

当代码被编译或解释时,你的“0.1”已经被四舍五入到该格式中最接近的数字,这会在计算发生之前导致一个小的舍入误差。

问题是new BigDecimal(-0.07);使用double文字来初始化BigDecimal- 所以错误仍然发生。使用BigDecimal采用 a 的构造函数String

于 2012-05-15T14:54:30.217 回答
5

您应该使用字符串构造函数来避免由于使用双精度数而导致的舍入错误:

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

使用双精度的构造函数的 Javadoc 提取- 强调我的:

  1. 此构造函数的结果可能有些不可预测。有人可能会假设用 Java 编写 new BigDecimal(0.1) 会创建一个正好等于 0.1 的 BigDecimal(未缩放的值 1,缩放为 1),但它实际上等于 0.1000000000000000055511151231257827021181583404541015625。这是因为 0.1 不能完全表示为双精度数(或者,就此而言,不能表示为任何有限长度的二进制分数)。因此,传递给构造函数的值并不完全等于 0.1,尽管看起来如此。
  2. 另一方面,String 构造函数是完全可预测的:编写 new BigDecimal("0.1") 会创建一个正好等于 0.1 的 BigDecimal,正如人们所期望的那样。因此,一般建议优先使用 String 构造函数。
于 2012-05-15T14:55:13.560 回答
3

问题就在这里:

BigDecimal velocity = new BigDecimal(-0.07);

-0.07不能完全表示为 a double,因此传递给BigDecimal构造函数的文字值最终与 . 略有不同-0.07BigDecimal只需采用该近似值并使用它运行,即可产生您所看到的结果。

尝试:

BigDecimal velocity = new BigDecimal(-7, 2);
BigDecimal afterMultiplyingBy200 = velocity.multiply( new BigDecimal(2, -2) );
于 2012-05-15T14:54:12.117 回答
1

根据文档ROUND_FLOOR向负无穷大四舍五入,因此四舍五入-14.000...-15.

于 2018-03-28T14:36:31.663 回答
0

你应该看看RoundingMode,尤其是HALF_UP

于 2012-05-15T14:54:16.823 回答