1. Overview
In this quick article, we’re going to have a look at why changing the sum order returns a different result.
2. Problem
When we look at the following code, we can easily predict the correct answer (13.22 + 4.88 + 21.45 = 39.55). What is easy for us, might be interpreted differently by the Java compiler:
double a = 13.22;
double b = 4.88;
double c = 21.45;
double abc = a + b + c;
System.out.println("a + b + c = " + abc); // Outputs: a + b + c = 39.55
double acb = a + c + b;
System.out.println("a + c + b = " + acb); // Outputs: a + c + b = 39.550000000000004
From a Mathematical point of view, changing the order of a sum should always give the same result:
(A + B) + C = (A + C) + B
This is true and works well in Java (and other computer programming languages) for integers. However, almost all CPUs use for non-integer numbers IEEE 754 binary floating point standard, which introduces inaccuracy when decimal number is stored as the binary value. Computers can’t represent all real numbers precisely.
When we change the order, we also change the intermediate value that is stored in the memory, and thus the result may differ. In the next example, we simply start with either sum of A+B or A+C:
double ab = 18.1; // = 13.22 + 4.88
double ac = 34.67; // = 13.22 + 21.45
double sum_ab_c = ab + c;
double sum_ac_b = ac + b;
System.out.println("ab + c = " + sum_ab_c); // Outputs: 39.55
System.out.println("ac + b = " + sum_ac_b); // Outputs: 39.550000000000004
3. Solution
Because of notorious inaccuracy of floating point numbers, double should never be used for precise values. This includes currency. For accurate values, we can use BigDecimal class:
BigDecimal d = new BigDecimal(String.valueOf(a));
BigDecimal e = new BigDecimal(String.valueOf(b));
BigDecimal f = new BigDecimal(String.valueOf(c));
BigDecimal def = d.add(e).add(f);
BigDecimal dfe = d.add(f).add(e);
System.out.println("d + e + f = " + def); // Outputs: 39.55
System.out.println("d + f + e = " + dfe); // Outputs: 39.55
Now we can see that in the both cases results were the same.
4. Conclusion
When working with decimal values, we always need to remember that floating point numbers are not represented correctly, and this can cause unexpected and unwanted results. When precision is required, we must use BigDecimal class.
As always, the code used throughout the article can be found over on GitHub.