spacitron
spacitron

Reputation: 2183

Performing repeated additions on float or double values produces unexpected results

I found a but in my code and after some searching I realized that some floating operations that I was performing produced the wrong results. So I typed the following loop:

 float f = 0f;
     for(int i =0; i<15; i++){
        f+= 10.1f;
        System.out.println(f);
}

But in the results I get unexpected additional decimal values:

10.1
20.2
30.300001
40.4
50.5
60.6
70.7
80.799995
90.899994
100.99999
111.09999
121.19999
131.29999
141.4
151.5

What is going on here and how do I prevent it?

Upvotes: 1

Views: 168

Answers (3)

Eric Postpischil
Eric Postpischil

Reputation: 222274

Two operations in your code cause errors:

  • The conversion of the source text 10.1f to float. This conversion is inexact because 10.1 cannot be exactly represented in the floating-point format Java uses.
  • The additions of 10.1f to previous values of f. In many of these additions, the exact sum will not fit completely in the floating-point format, so the result must be rounded.

You can modify the loop to avoid accumulating errors:

for (int i = 0; i < 15; i++)
{
    f = (i+1) * 101 / 10.f;
    System.out.println(f);
}

When you write the code in this way, f will still not be exactly 10.1•(i+1) in every iteration, but it will be the closest representable value. In this new version, (i+1) * 101 is calculated exactly, and 10.f is exactly 10 because 10 is representable in the floating-point format. This means that the only error is in the division operation. That operation will return the representable value closest to the exact result.

Java uses IEEE-754 binary floating-point, 32 bits for float and 64 bits for double. In those formats, a number is represented, basically, as an integer times a power of 2. In the 32-bit format, the integer must have magnitude less than 224. The closest you can get to 10.1 in the 32-bit format is 5295309•2-19, which is 10.1000003814697265625.

Upvotes: 1

Matt
Matt

Reputation: 3353

The numbers are stored as binary, and at a certain precision the representation between binary and decimal is not perfect, essentially leading to 'rounding errors'. If precision is of importance in this scenario, try using BigDecimal instead

Upvotes: 3

McAden
McAden

Reputation: 13972

You can't "prevent" it. What you need to do instead is expect it and compensate.

See: What Every Computer Scientist Should Know About Floating-Point Arithmetic

Upvotes: 3

Related Questions