java - BigDecimal 除法为某些计算返回错误的结果

标签 java bigdecimal

我读过一些关于 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)

由于您使用的是带有 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 以外的舍入模式。)

Try it online.

我不确定默认情况下它使用什么比例,但是您在 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")

Try it online.

然而,这与您的舍入问题有点无关,因为使用正确的 divide 方法将给出正确的结果,而不管您使用的 BigDecimal 初始化如何:

Try it online.

关于java - BigDecimal 除法为某些计算返回错误的结果,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56627367/

相关文章:

java - 生成某些实例变量的列表

java - java.math.MathContext 的使用

Java - 模数函数在处理大量数据时失败

java - 在 Java 中将 BigDecimal 字段作为 JSON 字符串值返回

java - android 应用程序上的 adfs 登录身份验证

java - 如果一些常量被多个类使用,如何设计

java - 如何仅关闭 ScheduledExecutorService 中选定的线程?

java - 未找到 Paypal 沙箱集成 API 凭据

java - 大十进制 : HALF_UP rounding with setScale

bigdecimal - 无法使用大数字作为参数从 web3 调用合约函数