java - 执行涉及非终止 BigDecimal 的算术

标签 java precision bigdecimal

当对 BigDecimal 进行除法时,结果可能是非终止的。因此,您必须提供 MathContext 或 RoundingMode/scale 作为除法运算的一部分。但是,根据算术中的运算顺序,精度损失可能会导致差异。

使用 BigDecimals 时如何避免由于精度损失而导致的差异(例如下面所示的示例)?很想知道其他人如何处理此类问题。

示例:

BigDecimal v1 = new BigDecimal("29.14");
BigDecimal v2 = new BigDecimal("12");
BigDecimal v3 = new BigDecimal("75");
System.out.println(v1.divide(v2, MathContext.DECIMAL64).multiply(v3).setScale(2, RoundingMode.HALF_UP));
// Prints: 182.12
System.out.println(v1.multiply(v3).divide(v2, MathContext.DECIMAL64).setScale(2, RoundingMode.HALF_UP));
// Prints: 182.13

在上面的示例中,根据操作顺序,结果会发生变化。

在测试时遇到这个示例之前,我从来没有认为 BigDecimal 运算的顺序是一个问题。现在,我陷入了困境:)

最佳答案

这是浮点算术的一个常见问题,无论是二进制还是十进制:除法可能不准确。因此,当您必须进行乘法和除法时,如果您先进行除法并且结果不准确,那么当您稍后进行乘法时,您将乘以第一个结果的误差。

在您的示例中,确切的结果是 182.125 ,它正好位于要舍入到 182.13 的边界内。当您先进行除法时,29.14/12 的确切结果是 2.428333...

对于 DECIMAL64 的 16 位数字,四舍五入为 2.428333333333333,当它乘以 75 时,得到 182.12499999999997,四舍五入为 182.12

那么这里可以做什么:

  • 如果不存在溢出风险,则先执行乘法,最后执行除法。例如,对于a/b*c/d,您应该计算(a*c)/(b*d)
  • 使用小数时尝试添加一位精度数字。这里您应该使用 3 个小数位的精度,因为初始数字是用 2 个 1 给出的。两种情况的结果都是 182.125

但无论如何,一旦您没有精确的结果,就有可能出现舍入误差,这是浮点计算所固有的。

关于java - 执行涉及非终止 BigDecimal 的算术,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48690076/

相关文章:

java - Android - 在 ExpandableListView 的每一行中的项目上设置监听器

java - 从异步任务获取空响应,响应代码为 200,这意味着成功

java - BigDecimal - 使用 new 或 valueOf

java - 对于大数字,从 DecimalFormat.format() 获取不正确的结果

java - MySQL Found_Rows() 返回正确值的两倍

java - 异常传播指南(Java)

c++ - 无法让我的计时器每毫秒计数一次以上

java - Oracle JDBC : underflow in double

C、FLT_MAX值大于32位?

java - 如何在 Java 中为太大和太小的数字获得非常高的精度?