执行涉及非终止BigDecimals的算法

时间:2018-02-08 16:19:11

标签: 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操作的顺序是个问题。现在,我陷入了这种困境:)

1 个答案:

答案 0 :(得分:1)

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

在您的示例中,确切的结果是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

但无论如何,只要你没有确切的结果,你就有可能得到舍入误差,这是浮点计算所固有的。