我读过一些关于浮点数及其数学的问题。对我来说,问题似乎只发生在更大的规模上(即 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)
由于您使用的是带有BigDecimal
和int
参数的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
方法都会给出正确的结果:
在线试用。