我读过一些关于 float 及其数学的问题。在我看来,问题只发生在更大的范围内(即 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
方法的可用参数:
除法(BigDecimal 除数)
divide(BigDecimal divisor, int roundingMode)
除法(BigDecimal 除数,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)
新的 BigDecimal(2.8)
new BigDecimal("2.8")
new BigDecimal(2.8)
仍然会出现浮点错误,所以我建议像您已经使用的那样使用 BigDecimal.valueOf(2.8)
,或者使用 String构造函数 new BigDecimal("2.8")
。
然而,这与您的舍入问题有点无关,因为使用正确的 divide
方法将给出正确的结果,而不管您使用的 BigDecimal
初始化如何:
关于java - BigDecimal 除法为某些计算返回错误的结果,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56627367/