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.

Chris Martin
  • 30,334
  • 10
  • 78
  • 137
Petersoj
  • 51
  • 9

2 Answers2

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!

Matthew Gunn
  • 4,451
  • 1
  • 12
  • 30
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)
Andreas
  • 154,647
  • 11
  • 152
  • 247
MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • You could have just printed `waittime` *without* dividing by `1000`, and it would have showed the problem without all those zeroes. – Andreas Dec 09 '15 at 03:26
  • @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) – MadProgrammer Dec 09 '15 at 03:29
  • I'm a math head, I guess. ;-) – Andreas Dec 09 '15 at 03:30