大十进制除法返回某些计算的错误结果



我读过一些关于浮点数及其数学的问题。对我来说,问题似乎只发生在更大的规模上(即 10+ 位小数)。现在我的问题已经发生在小数点后 2 位,并且在少数情况下它非常大:

以下代码:

System.out.println(
String.format("%.2f", BigDecimal.valueOf(2.8).divide(BigDecimal.valueOf(3.87), 2).doubleValue()));
System.out.println(
String.format("%.2f", BigDecimal.valueOf(2.41).divide(BigDecimal.valueOf(2.73), 2).doubleValue()));

分别生成此输出:

0.80
0.89

如果我手动执行计算(使用谷歌计算),我会得到以下结果:

0.72351421188
0.88278388278

对于第一个计算,结果真的很大(~0.08 off),而对于第二个计算,结果非常低(~0.01 off)。

对于为什么第一个结果那么大,是否有一些理智的解释?或者任何使用BigDecimal获得正确结果的方法?

请注意,

System.out.println(2.8/3.87);

实际返回正确的结果 (0.7235142118863048)。

另请注意,String.format的东西仅用于检查结果是否被它更改,但事实并非如此。BigDecimal.valueOf(2.8).divide(BigDecimal.valueOf(3.87), 2).doubleValue()产生完全相同的结果。

问题是您使用了错误的BigDecimal#divide来舍入到小数点后 2 位。以下是BigDecimal#divide方法的可用参数:

  • divide(BigDecimal divisor)
  • divide(BigDecimal divisor, int roundingMode)
  • divide(BigDecimal divisor, MathContext mc)
  • divide(BigDecimal divisor, RoundingMode roundingMode)
  • divide(BigDecimal divisor, int scale, int roundingMode)
  • divide(BigDecimal divisor, int scale, RoundingMode roundingMode)

由于您使用的是带有BigDecimalint参数的divide,因此它使用divide(BigDecimal divisor, int roundingMode),其中您的2是舍入模式而不是刻度。在这种情况下,2实际上是ROUND_CEILING的,并且未指定比例。

相反,您必须使用divide(BigDecimal divisor, int scale, int roundingMode)divide(BigDecimal divisor, int scale, RoundingMode roundingMode).因此,请将您的呼叫更改为:

System.out.println(BigDecimal.valueOf(2.8).divide(BigDecimal.valueOf(3.87), 2, 
BigDecimal.ROUND_HALF_UP));
System.out.println(BigDecimal.valueOf(2.41).divide(BigDecimal.valueOf(2.73), 2, 
BigDecimal.ROUND_HALF_UP));

(请随意使用ROUND_HALF_UP以外的舍入模式。

在线试用。

我不确定默认情况下它使用什么比例,但是您在2中指定的ROUND_CEILING导致了计算中的问题。


至于提到的注释,有三种可能的方法可以使用指定的值创建BigDecimal

  • BigDecimal.valueOf(2.8)
  • new BigDecimal(2.8)
  • new BigDecimal("2.8")

new BigDecimal(2.8)仍然给出浮点错误,所以我建议像您已经做的那样使用BigDecimal.valueOf(2.8),或者 String 构造函数new BigDecimal("2.8")

在线试用。

但是,这与您的舍入问题无关,因为无论您使用何种BigDecimal初始化,使用正确的divide方法都会给出正确的结果:

在线试用。

最新更新