1. Overview
1.概述
In this quick article, we’re going to have a look at why changing the sum order returns a different result.
在这篇快速文章中,我们要看一下为什么改变和的顺序会得到不同的结果。
2. Problem
2.问题
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:
当我们看下面的代码时,我们可以很容易地预测出正确的答案(13.22 + 4.88 + 21.45 = 39.55)。对我们来说很容易的事情,在Java编译器中可能会有不同的解释。
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
(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.
在Java(和其他计算机编程语言)中,对于整数来说,这一点是正确的,而且效果很好。然而,几乎所有的CPU都对非整数使用IEEE 754二进制浮点标准,当十进制数被存储为二进制值时,会引入不准确的情况。计算机不可能精确地表示所有的实数。
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:
当我们改变顺序时,我们也改变了存储在内存中的中间值,因此结果可能不同。在下一个例子中,我们只是从A+B或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
3.解决方案
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:
由于浮点数的不准确的恶名,double绝不应该被用于精确的数值。这包括货币。对于精确值,我们可以使用BigDecimal类。
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
4.结论
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.
在处理十进制数值时,我们始终需要记住,浮点数的表示方法不正确,这可能会造成意想不到的、不想要的结果。当需要精确度时,我们必须使用BigDecimal类。
As always, the code used throughout the article can be found over on GitHub.
一如既往,文章中所使用的代码可以在GitHub上找到over。