-1
public class yyy
{
    public static void main(String[] args)
    {
        double sum = 0;
        float d = 0;
        while (d != 10.0) {
            d += 0.1;
            sum += sum + d;
        }
        System.out.print("The sum is: "+sum);
   }
}
  • Where is the error which causes the infinite loop?
  • Why?
  • I thought that the type is the problem but its not; I changed d to double but nothing changed.
Alexis C.
  • 91,686
  • 21
  • 171
  • 177
sully
  • 31
  • 2
  • This code will not compile. – fvrghl Oct 26 '13 at 12:09
  • `public static void main(String\[\] args)` :( – Suresh Atta Oct 26 '13 at 12:10
  • Why do you need the `Scanner` and the user input? – Raul Rene Oct 26 '13 at 12:13
  • `d` is never `10.0` I suggest you print them out in the loop or debug your code in a debugger. In short, never use a floating point for a loop counter. Also you shouldn't mix your `float` and `double` as there is no sane reason to do this here. – Peter Lawrey Oct 26 '13 at 13:07
  • 4
    **Downvoters:** Clearly the OP is new to SO and unfamiliar with markdown. But her question is a good one. It explores the fundamentals of the way data is stored in computers, and it highlights a problem that plagues far too many experienced programmers. Kudos to @ZouZou for reformatting. If you downvoted solely on the basis of appearance, or because the question is a basic one, please recognize the relevance of the question, albeit a beginner-level one, and consider removing your downvotes. – Adam Liss Oct 26 '13 at 13:19

5 Answers5

5

It's an infinite loop because d is never equal to 10.0. Why not? Because floating point numbers are not perfectly precise, tiny errors creep in as you add 0.1 to d. You can watch this in the debugger, stepping through the code. The kind of floating point used in Java is IEEE-754, more info here.

Here are the values as d approaches, and then skips over, 10.0:

d = 9.4
d = 9.5
d = 9.6
d = 9.700001
d = 9.800001
d = 9.900002
d = 10.000002
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
3

What makes the loop infinite is the imprecision of float's representation of 0.1: since it is not an exact sum of negative powers of two, adding it 100 times to itself does not make exactly 10.0, even though in theory it does. The number is very close, but since you use !=, the loop never stops.

Replace with

while (d < 10.05)

to fix the infinite loop problem. However, this is not the most readable approach: using an int for your loop counter would be a lot cleaner.

Note that if you used a different step, for example, 0.125, your loop would have worked. This is because 0.125 is 2 ^ -3, which has an exact representation as a float.

I changed d to double but nothing changed.

The double type has the same logical representation as float, but with more bits to extend its range and precision. Using BigDecimal would have fixed the problem, though, because this data type uses a different representation, which allows 0.1 to be represented exactly.

The main point that you should learn from this exercise is that you need to be very careful when comparing floating point values for equality and inequality.

Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
  • `d < 10.0` is not that great because you don't know if you will have a value close to 10 inside the loop and will terminate the loop. You could use `d < 10.05` but using an integer loop counter is much safer/clearer. – Peter Lawrey Oct 26 '13 at 13:09
  • @PeterLawrey Thanks! I agree with both your suggestions - I edited to reflect both of them in the answer. – Sergey Kalinichenko Oct 26 '13 at 13:16
2

The infinite loop is due to the inexact way computers represent floating-point numbers.

A computer can dedicate only a certain number of bits to each variable. You can think of this as storing a limited number of digits after the decimal place. In base ten you can represent 1/10 exactly as 0.1, so you can write d with perfect precision as it progresses through 0.0, 0.1, 0.2, ..., 10.0. But imagine incrementing by 1/7. This is a repeating decimal (0.142857142857...) and therefore cannot be represented precisely in base 10 with a limited number of digits. If the computer could store only 2 decimal places for each variable, then d would look something like this:

Fraction   Actual Value   Stored Value
   1/7      0.142857...        0.14
   2/7      0.285714...        0.28
   3/7      0.428571...        0.42
   4/7      0.571428...        0.56   ← mismatch
   5/7      0.714285...        0.70
   6/7      0.857142...        0.84
   7/7      1.0                0.99
Note the rounding errors in every value from 4/7 onward. (Actually, every value has a rounding error due to truncation; it's just more obvious when the digits don't match.) The important thing to notice is that it doesn't matter how many digits we store; unless it's infinite, there will always be a rounding error.

So, in base ten, incrementing a variable by 0.1 is simple and "clean" because the number can be represented exactly with a limited number of digits. But that's not the case for numbers that are represented by repeating decimals, like 1/6, 1/7, 1/13, and so on.

Computers store numbers in binary (base 2), but the concept is exactly the same. The fraction 1/10 does not have an exact representation in base 2. The computer must represent every number by adding together different powers of 2: numbers like 8, 4, 2, 1, ½ ¼, &frac18;, and so on. For example:

15 = 8 + 4 + 2 + 1         = 1111b
10 = 8 + 0 + 2 + 0         = 1010b
2½ = 0 + 0 + 2 + 0 + ½     = 0010.1b
 ¾ = 0 + 0 + 0 + 0 + ½ + ¼ = 0010.11b 

But we can't represent 1/10 exactly using only powers of 2:

1/10 = 0/2 + 0/4    + 0/8    + 1/16   + 1/32   + 0/64 + 0/128 + 1/256 +
     1/512 + 0/1024 + 0/2048 + 1/4096 + 1/8192 + ...
1/10 = 0.0001100110011...b

Again, imagine we have a limited number of decimal places. You'll see that no matter how many we use, we'll eventually generate a rounding error if we continue to add 1/10. And that's exactly what happens in your program: repeatedly adding the binary representation of 1/10 will generate a rounding error before the sum reaches 10.0, so the condition d != 10.0 will always be true.

Because of this, when working with floating-point numbers, the best practice is as several others suggested: never test floating-point variables for equality; always use inequalities. You can eliminate the infinite loop with while (d < 10.0).

Adam Liss
  • 47,594
  • 12
  • 108
  • 150
  • The comment about changing from `float` to `double` was added while I was answering. Using `double` increases the precision (by increasing the number of binary "decimal places" that are stored), but there will _always_ be a rounding error, unless you can use an _infinite_ amount of storage for each number! – Adam Liss Oct 26 '13 at 13:21
0

Doubles and floats have what is known as floating-point precision, i.e. besides the 0.1 you are adding there are many more decimal places which you do not see. As T.J. Crowder said, you can see the values by stepping through with a debugger. My suggestion is to work with ints when comparing values, otherwise check out this suggestion.

Community
  • 1
  • 1
Jurgen Camilleri
  • 3,559
  • 20
  • 45
0

The best way to do this loop is using fixed precision. i.e. use an integer value assuming one decimal place which you add at the end. As you can see the room for error is very small.

int sum = 0;
for(int i = 0; i <= 100; i++)
   sum += i;
// sum is 10x the value you need.
System.out.print("The sum is: " + sum/10.0); 

The reason this helps is that otherwise you are performing a long series of imprecise calculations and looking for an exact outcome of 10.0 which is possible by highly unlikely. i.e. the cumulative error could be zero, but you wouldn't want to assume it is for different values.

Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130