0

I just made a program that is kind of weird, but bear with me:

public class Hello {

public static void main(String[] args) {
    double waittime = 10000;
    int index = 0;
    while(true){
        index++;
        System.out.println(String.valueOf(index) + "\t" + String.valueOf(waittime/1000) + " seconds");
        waittime = waittime/2;
        if(waittime <= 0){
            break;
        }
        try {
            Thread.sleep((long) waittime);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

Here is the output:

1   10.0 seconds
2   5.0 seconds
3   2.5 seconds
4   1.25 seconds
5   0.625 seconds
6   0.3125 seconds
7   0.15625 seconds
8   0.078125 seconds
9   0.0390625 seconds
10  0.01953125 seconds
11  0.009765625 seconds
12  0.0048828125 seconds
13  0.00244140625 seconds
14  0.001220703125 seconds
15  6.103515625E-4 seconds

//.... more really small numbers ....

1075    4.9E-323 seconds
1076    2.5E-323 seconds
1077    1.0E-323 seconds
1078    4.9E-324 seconds
1079    4.9E-324 seconds
1080    0.0 seconds
1081    0.0 seconds
1082    0.0 seconds
1083    0.0 seconds
1084    0.0 seconds
1085    0.0 seconds
1086    0.0 seconds
1087    0.0 seconds
1088    0.0 seconds

As you can see, in my class I am checking if the "waittime" variable is less than or equal to zero, but towards the end of the output, the "index" variable is printed out 9 more times than it should've.

I don't really understand why this happens or how it can be avoided. I am not here to have this code fixed, I just want to understand why this happens.

12
  • The reason it doesn't work is math. Commented Dec 9, 2015 at 3:14
  • 0.01 isn't zero or less Commented Dec 9, 2015 at 3:14
  • 1
    @Petersoj Since when? Commented Dec 9, 2015 at 3:16
  • 1
    Since your if is based on waittime and not waittime/1000 try printing String.valueOf(waittime) instead of String.valueOf(waittime/1000) Commented Dec 9, 2015 at 3:16
  • 3
    Dividing a number by 2 repeatedly will get you closer and closer to zero but it will never actually reach 0. The only reason that this program eventually terminates is that you reach the limits of precision of the double value. Commented Dec 9, 2015 at 3:17

2 Answers 2

2

So I modified the code a little to add in a NumberFormat (and reduce the number of loops)

double waittime = 100;
int index = 0;
NumberFormat nf = NumberFormat.getNumberInstance();
nf.setMinimumFractionDigits(1000);
while (true) {
    index++;
    System.out.println(String.valueOf(index) + "\t" + (waittime / 1000) + "; " + nf.format(waittime) + " seconds");
    waittime = waittime / 2;
    if (waittime <= 0) {
        break;
    }
    try {
        Thread.sleep((long) waittime);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

Which outputs...

.
.
.
1072    4.9E-324; 0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000039530000000000000000 seconds
1073    0.0; 0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000019760000000000000000 seconds
1074    0.0; 0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009900000000000000000 seconds
1075    0.0; 0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004940000000000000000 seconds
1076    0.0; 0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002470000000000000000 seconds
1077    0.0; 0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001240000000000000000 seconds
1078    0.0; 0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000590000000000000000 seconds
1079    0.0; 0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000 seconds
1080    0.0; 0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000150000000000000000 seconds
1081    0.0; 0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000 seconds
1082    0.0; 0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000049000000000000000 seconds

So, as you can see (waittime / 1000) eventually prints 0.0, but the actual value (at least to 1000 decimal digits) isn't zero.

So it's just an artifact of how Java is representing the raw value

aengus comments is a good indication of why the loop will eventually break though


Alternate: Just show waittime without dividing by 1000, which is causing underflow early in the display:

1078    4.9E-324 seconds, (6.176E-321 ms)
1079    4.9E-324 seconds, (3.09E-321 ms)
1080    0.0 seconds, (1.54E-321 ms)
1081    0.0 seconds, (7.7E-322 ms)
1082    0.0 seconds, (3.85E-322 ms)
1083    0.0 seconds, (1.93E-322 ms)
1084    0.0 seconds, (1.0E-322 ms)
1085    0.0 seconds, (4.9E-323 ms)
1086    0.0 seconds, (2.5E-323 ms)
1087    0.0 seconds, (1.0E-323 ms)
1088    0.0 seconds, (4.9E-324 ms)
Sign up to request clarification or add additional context in comments.

3 Comments

You could have just printed waittime without dividing by 1000, and it would have showed the problem without all those zeroes.
@Andreas It would only print the significant notation, all the "zeros" is a really nice demonstration of the point, which is more human readable (at least for non-math heads like me)
I'm a math head, I guess. ;-)
2

Not sure what you're expecting? It waits 10000, then 500, then 250, then 125, then 62.5, then 31, then 15, then 8, then 4, then 2, then 1, then 1/2, then 1/4, then 1/8...

For a related story, checkout Zeno's paradox

In mathematics, repeatedly dividing by two will get you infinitely close to 0 without ever reaching it.

In the real world of floating point, eventually you may trigger a roundoff to zero. Also, printing out the value of a double precision floating point may also be imperfect/with roundoff, which is why it shows 0 even if in the machine, it really isn't. And as Andreas pointed out, you divided by 1000 too!

2 Comments

OP wants to know why it prints 0.0 a few times before stopping (if it stops)
It shows 0 early because the output is divided by 1000.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.