Reputation: 860
When dividing a BigDecimal, it's possible that the result will be non-terminating. As a result, you must provide a MathContext or RoundingMode/scale as part of the division operation. However, depending on the order of operations in your arithmetic, the loss of precision can result in discrepancies.
How can discrepancies due to precision loss (such as example shown below) be avoided when using BigDecimals? Curious to know how others have dealt with this type of issue.
Example:
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
In the example above, depending on the order of operations, the result changes.
Before running into this example while testing, I would have never considered the order of BigDecimal operations to be a problem. Now, I'm left with this dilemma :)
Upvotes: 0
Views: 136
Reputation: 149125
That is a common problem with floating point arithmetics, be it binary or decimal: a division may be unaccurate. So when you have to do a multiplication and a division, if you do the division first and the result is unaccurate, when you later do the multiplication you multiply the error of the first result.
In you example, the exact result is 182.125 which is just in the border to be rounded to 182.13 . When you make the division first, the exact result of 29.14/12 is 2.428333...
With the 16 digits of DECIMAL64, it is rounded to 2.428333333333333 and when it is multiplied with 75, it gives 182.12499999999997 which will be rounded to 182.12
So what can be done here:
a/b*c/d
you should compute (a*c)/(b*d)
But anyway, as soon as you have not exact results, there is a risk that you get a rounding error, it is inherent to floating point computation.
Upvotes: 1