2

I am executing the following code in java but i got two different answers for what should be the same number mathematically.

public class TestClass {

    public static void main(String[] args) {
        
        double a=0.01;
        double b=4.5;
        double c=789;       
        System.out.println("Value1---->"+(a*b*c));
        System.out.println("Value2---->"+(b*c*a));      
    }
}

Output:

Value1---->35.504999999999995
Value2---->35.505
ZygD
  • 22,092
  • 39
  • 79
  • 102
  • 1
    Use `printf` to get it to output correctly – Ascalonian Mar 18 '15 at 12:29
  • 1
    the `printf` comment is valid if it is the format that you don't like. However - these calculations ***give different answers*** - so `(a*b*c) == (b*c*a)` will be `false` with these particular values. So it is best to understand why as per the answers below, rather than "mask" the problem by reformatting the output. – J Richard Snape Mar 18 '15 at 12:39
  • 1
    How does this get seven upvotes and a dozen answers... this question is asked every other week. Once in every programmer's life they come to discover that computer math != math math and feel the need to have this question answered. We don't need one question and ten answers every time this happens for every programmer on the internet. – J... Mar 18 '15 at 12:59
  • @J... You'll see that many of the users answering this have sufficiently low rep that this is their first time encountering such a question. – Gabe Mar 18 '15 at 17:10

10 Answers10

4

Floating point numbers have a certain precision. Some fractions can not be displayed correctly with floating point numbers, that's why rounding errors can occur.

The results are different because of the precedence of the calculations. Each of your calculations consists of two multiplications. The multiply * operator in Java has a left to right associativity. That means that in (a*b*c), a*b is calculated first and then multiplied by c as in ((a*b)*c). One of those calculation chains happens to produce a rounding error because a number in it simply can't be represented as a floating point number.

ZygD
  • 22,092
  • 39
  • 79
  • 102
David
  • 3,392
  • 3
  • 36
  • 47
2

Essentially, Java uses binary floating point values to handle all of its decimal based operations. As mentioned, in another answer, here is a link to the IEEE 754 that addresses the issue you've encountered. And as also mentioned in Joshua Bloch's Effective Java, refer to item 48 "Avoid float and double if exact answers are required":

In summary, don’t use float or double for any calculations that require an exact answer. Use BigDecimal if you want the system to keep track of the decimal point and you don’t mind the inconvenience and cost of not using a primitive type.

heez
  • 2,029
  • 3
  • 27
  • 39
  • 1
    +int(PI/3) for citing JB - many of beginner's errors can be solved by simply reading through EJ, 2nd... –  Mar 18 '15 at 13:08
1

It is because type double is an approximation. Double in Java denotes to IEEE 754 standart type decimal64. To resolve this problem use Math.round() or either BigDecimal class.

Maksim
  • 264
  • 7
  • 20
1

Multiplication of floating points uses a process that introduces precision errors.

To quote Wikipedia:

"To multiply, the significands are multiplied while the exponents are added, and the result is rounded and normalized."

Java multiplies from left to right. In your example, the first parts (a * b and b * c) actually produce no precision errors.

So your final multiplications end up as:

System.out.println("Value1---->" + (0.045 * 789));
System.out.println("Value2---->" + (3550.5 * 0.01));

Now, 0.045 * 789 produces a precision error due to that floating point multiplication process. Whereas 3550.5 * 0.01 does not.

David Lavender
  • 8,021
  • 3
  • 35
  • 55
0

'Cause double * double will be double, and that not totally precise.

Try the following code:

System.out.println(1.0-0.9-0.1) // -2.7755575615628914E-17

If you want totally precise real numbers, use BigDecimal instead!

Nagy Vilmos
  • 1,878
  • 22
  • 46
0

This is because double has finite precision. Binary representation can't store exactly the value of for example 0.01. See also wikipedia entry on double precision floating point numbers.

Order of multiplication can change the way that representation errors are accumulated.

Consider using BigDecimal class, if you need precision.

tmp
  • 1,079
  • 9
  • 16
0

See this reply from 2011 Java:Why should we use BigDecimal instead of Double in the real world?

It's called loss of precision and is very noticeable when working with either very big numbers or very small numbers.

See the section

Decimal numbers are approximations

And read down

Community
  • 1
  • 1
0

As JavaDoc the double is a floating point type, and it's imprecise by nature. That why two exactly identical operation will wield different results, since the float point type (double) is an approximation.

See http://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html :

double: The double data type is a double-precision 64-bit IEEE 754 floating point. Its range of values is beyond the scope of this discussion, but is specified in the Floating-Point Types, Formats, and Values section of the Java Language Specification. For decimal values, this data type is generally the default choice. As mentioned above, this data type should never be used for precise values, such as currency.

See also the wikipedia http://en.wikipedia.org/wiki/Floating_point :

The floating-point representation is by far the most common way of representing in computers an approximation to real numbers.

JFPicard
  • 5,029
  • 3
  • 19
  • 43
0

http://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html Quote: "double: The double data type is a double-precision 64-bit IEEE 754 floating point."
When you dig into IEEE 754 you will understand how doubles are stored in memory.
For such calculations I would recommend http://docs.oracle.com/javase/8/docs/api/java/math/BigDecimal.html

0

As mentioned, there is an issue with the floating precision. You can either use printf or you can use Math.round() like so (change the number of zeros to affect precision):

System.out.println("Value 1 ----> " + (double) Math.round((a*b*c) * 100000) / 100000);
System.out.println("Value 2 ----> " + (double) Math.round((b*c*a) * 100000) / 100000);

Output

Value 1 ----> 35.505
Value 2 ----> 35.505

Ascalonian
  • 14,409
  • 18
  • 71
  • 103