首页 > 解决方案 > 比较两个向下舍入到特定小数的双变量时是否存在精度问题?

问题描述

我正在尝试比较使用 BigDecimals 和 RoundingMode 向下舍入到特定小数位的两个双精度类型变量。

例如,当比较 3.1456 和 3.145(在 if 语句中舍入到小数点后 3 位)时,我的 if 语句只有在两个值彼此相等时才应该返回 true。

但是,它不适用于我的代码

BigDecimal one = new BigDecimal(3.1456).setScale(3, RoundingMode.DOWN);
BigDecimal two = new BigDecimal(3.145).setScale(3, RoundingMode.DOWN);

if (one.equals(two)) {
    System.out.println("Both variables are equal");
} else {
    System.out.println("Both variables are not equal");
}

输出逻辑为假。所以我做了一些检查,看看我的变量被四舍五入到的值。分别是 3.145 和 3.144,这是出乎意料的。找不到3.144的原因...

接下来,我通过使用 DecimalFormat 和 RoundingMode 使用另一种方法来完成上述操作。它工作得很好。

DecimalFormat df0 = new DecimalFormat("#.###");
DecimalFormat df1 = new DecimalFormat("#.###");
df0.setRoundingMode(RoundingMode.DOWN);
df1.setRoundingMode(RoundingMode.DOWN);

if (df0.format(num0).equals(df1.format(num1))) {
    System.out.println("Both variables are equal")
} else {
    System.out.println("Both variables are not equal")
}

上面的方法效果很好!BigDecimal 将这两个值都计算为 3.145。

与使用 DecimalFormat 相比,我无法理解为什么使用 BigDecimal 执行任务时它不起作用。与双类型变量的工作方式有关吗?像 (double)(0.2-0.1) 这样的结果是 0.09999999...而不是 0.1?

请给我一个更好的解决方案和理解!太感谢了!

标签: java

解决方案


如文件所述,这BigDecimal(double)是不可预测的:

此构造函数的结果可能有些不可预测。有人可能会假设用 Java 编写 new BigDecimal(0.1) 会创建一个正好等于 0.1 的 BigDecimal(未缩放的值 1,缩放为 1),但它实际上等于 0.10000000000000000555511151231257827021181583404541015625。这是因为 0.1 不能完全表示为双精度数(或者,就此而言,不能表示为任何有限长度的二进制分数)。因此,传递给构造函数的值并不完全等于 0.1,尽管看起来如此。


他们建议使用BigDecimal(String)作为替代方案:

另一方面,String 构造函数是完全可预测的:编写 new BigDecimal("0.1") 会创建一个与 0.1 完全相等的 BigDecimal,正如人们所期望的那样。因此,一般建议优先使用 String 构造函数。

来自以下文档BigDecimal(String)

这通常是将 float 或 double 转换为 BigDecimal 的首选方法,因为它不会受到 BigDecimal(double) 构造函数的不可预测性的影响。


推荐阅读