当对 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/