分割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个。在两种情况下,结果都是182.125


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

09-10 05:42