93

I had a simple bit of code that was supposed to be an endless loop since x will always be growing and will always remain larger than j.

int x = 5;
int y = 9;
for (int j = 0; j < x; j++) {
   x = x + y;
}
System.out.println(y);

but as is, it prints y and does not loop endlessly. I cannot figure out why. However, when I adjust the code in the following manner:

int x = 5;
int y = 9;
for (int j = 0; j < x; j++) {
    x = x + y;
    System.out.println(y);
}
System.out.println(y);

It becomes an endless loop and I have no idea why. Does java recognize its an endless loop and skip it in the first situation but has to execute a method call in the second so it behaves as expected? Confused :)

HopefullyHelpful
  • 1,652
  • 3
  • 21
  • 37
Omar
  • 1,081
  • 1
  • 9
  • 14
  • 4
    The second loop is endless because the upper bound `x` grows _faster_ than the loop variable `j`. In other words, `j` will never hit an upper bound, hence the loop will run "forever." Well, not forever, you'll get an overflow at some point most likely. – Tim Biegeleisen Feb 01 '17 at 04:53
  • @TimBiegeleisen Huh? In the first loop the upper bound also grows faster than the loop variable, no? – Robby Cornelissen Feb 01 '17 at 04:55
  • 3
    @TimBiegeleisen The only difference is the `sysout`. Its something else. – Pavan Kumar Feb 01 '17 at 04:55
  • 75
    It's not an endless loop, it's just that it takes 238609294 times to loop to come out of the for loop in the first case and the second time it prints the value of `y` 238609294 times – N00b Pr0grammer Feb 01 '17 at 04:58
  • @N00bPr0grammer Good catch. Overflow at work. Or is it underflow? :) – Robby Cornelissen Feb 01 '17 at 04:59
  • @RobbyCornelissen :) – N00b Pr0grammer Feb 01 '17 at 05:00
  • both are not endless loop.. seocond one print y untill x reaches to max int value – Abhishek Singh Feb 01 '17 at 05:08
  • 13
    one word answer: **overflow** – qwr Feb 01 '17 at 06:20
  • 4
    In a perfect (theoretical/imaginary) computer it would run forever. In real life your variables have limits. – Tero Lahtinen Feb 01 '17 at 07:58
  • Is there a tool to see Java bytecode, similar to godbolt for C assembly? Then you could tell if the loop is executed or not. I searched but couldn't find anything handy. – Francesco Dondi Feb 01 '17 at 09:17
  • Btw in C and C++ endless loops are indeed undefined behaviour and can be optimized out (of course even worse things may happen). – CodesInChaos Feb 01 '17 at 10:27
  • 20
    Amusingly, `System.out.println(x)` instead of `y` at the end would have showed instantly what the problem was – JollyJoker Feb 01 '17 at 10:54
  • @JollyJoker it could have changed the optimization performed by the compiler, as the loop would now have a side effect. – Francesco Dondi Feb 01 '17 at 11:00
  • 9
    @TeroLahtinen no, it would not. Read the Java language spec if you have doubts what is int type. It is hardware independent. – 9ilsdx 9rvj 0lo Feb 01 '17 at 11:37
  • 3
    @FrancescoDondi Theoretically yes, but in practice he would have seen a negative `x` and instantly figured out what happened. – JollyJoker Feb 01 '17 at 11:53
  • 4
    @9ilsdx9rvj0lo Your point is that even with imaginary, non-resource bound computer we would have overflow, because the max value is defined in spec, did I understand correctly? – Tero Lahtinen Feb 01 '17 at 12:44
  • 3
    @TeroLahtinen yes. This has absolutely nothing to do with the computer (except the slowness in `println` causing the second option to _seem_ endless) but to do with the Java Language Specification **requiring** that `int` is a `32` bit singed two's complement number. This places hard bounds on the maximum (and minimum) values of `int`. – Boris the Spider Feb 01 '17 at 13:39
  • @9ilsdx9rvj0lo Java is "real life," not a theoretical machine. Tero is obviously referring to a Turing machine or what have you. – jpmc26 Feb 02 '17 at 00:07
  • 1
    @jpmc26 the question is about Java! – 9ilsdx 9rvj 0lo Feb 02 '17 at 07:37
  • 3
    @9ilsdx9rvj0lo That's Tero's point, though: this is Java running on a real machine, as opposed to a theoretical machine that could increment numbers indefinitely. He/she is contrasting the OP's expectation of behavior like the latter when that's impossible in the real world. It's a good point that shouldn't be dismissed so readily. – jpmc26 Feb 02 '17 at 08:02
  • @jpmc26 and there Tero is obviously wrong, because his expectations doesn't match reality – 9ilsdx 9rvj 0lo Feb 02 '17 at 08:36
  • 2
    @9ilsdx9rvj0lo: Step back and take another look at Tero's comment. He is quite obviously contrasting "theoretical / imaginary" with "real life" (where variables have limits), so your attacking his statement regarding the "theoretical / imaginary" for "not matching reality" (where variables have limits) and then insisting on *him* being wrong is a bit funny. ;-) – DevSolar Feb 02 '17 at 10:18
  • @DevSolar no, Tero simply seemed not to fully understand the concept of int in java, and he thought it to be determined by underlying hardware, just like in C. The question is about Java, so comments speculating about features of other languages would simply be off topic, and if made consciously, they would be trolling. – 9ilsdx 9rvj 0lo Feb 02 '17 at 10:25
  • @9ilsdx9rvj0lo: The limitation of `int` in Java is a direct result of computers being *not* "theoretical / imaginary", but limited by real-life constraints. If the limitless value range Tero pointed out *were* feasible, Java would be just as unlimited in its `int` values as C, C++, or what-have-you. And even if he *were* trolling, you'd be feeding him. And now I'll stop feeding *you*. ;-) – DevSolar Feb 02 '17 at 10:33
  • 3
    Yes, @9ilsdx9rvj0lo is correct, I was wrong in my comment, with Java (unlike some other languages) also Turing-type-of-machine and any other running Java would overflow. Thank you all for correcting me. – Tero Lahtinen Feb 02 '17 at 13:35
  • Somebody correct me if I'm wrong, but I think it's worth mentioning that no compiler can detect an endless loop. I definitely remember having a proof about it in algorithms class. – Abid Rizvi Feb 04 '17 at 23:50
  • 1
    @FrancescoDondi There is, it's called `javap` and it's bundled with the JDK (if you have `javac`, you have it too) – assembly_wizard Feb 07 '17 at 19:13
  • @AbidRizvi You're thinking of the halting problem. – jpmc26 Apr 19 '17 at 03:25

6 Answers6

160

Both of the examples are not endless.

The issue is the limitation of int type in Java (or pretty much any other common language). When the value of x reaches 0x7fffffff, adding any positive value will result in overflow and the x becomes negative, therefore lower than j.

The difference between the first and second loop is that the inner code takes much more time and it would take probably several minutes until x overflows. For the first example it may take less than second or most probably the code will be removed by optimizer as it doesn't have any effect.

As mentioned in the discussion, the time will heavily depend on how OS buffers the output, whether it outputs to terminal emulator etc., so it can be much higher than few minutes.

ivant
  • 3,909
  • 1
  • 25
  • 39
Zbynek Vyskovsky - kvr000
  • 18,186
  • 3
  • 35
  • 43
  • 48
    I just tried a program (on my laptop) that prints a line in a loop. I timed it, and it was able to print approximately 1000 lines/second. Based on N00b's comment that the loop will execute 238609294 times, it will take about 23861 seconds for the loop to terminate--over 6.6 hours. A tad more than "several minutes". – ajb Feb 01 '17 at 05:29
  • 11
    @ajb: Depends on the implementation. IIRC `println()` on Windows is a blocking operation, whereas on (some?) Unix it's buffered so goes much faster. Also try using `print()`, which buffers until it hits a `\n` (or the buffer fills, or `flush()` is called) – BlueRaja - Danny Pflughoeft Feb 01 '17 at 08:40
  • 6
    Also it depends on the terminal showing the output. See http://stackoverflow.com/a/21947627/53897 for an extreme example (where the slowing down was due to word wrapping) – Thorbjørn Ravn Andersen Feb 01 '17 at 17:40
  • 1
    Yes, it's buffered on UNIX, but it's still blocking. Once the 8K or so buffer fills, it will block until there's room. The speed will be heavily dependent on how quickly it's consumed. Redirecting output to /dev/null will be the fastest, but having it sent to the terminal, the default, will require graphics updates to the screen and much more compute power as it renders the fonts slowing it down. – penguin359 Feb 01 '17 at 19:59
  • @penguin359 : that's absolutely correct, terminal will definitely slow it down terribly. About the file - the buffer is flushed to system call only and the OS has is own buffers which are big enough to cache the full output so I wouldn't expect significant slowdown. – Zbynek Vyskovsky - kvr000 Feb 02 '17 at 03:38
  • 2
    @Zbynek oh, probably so, but that reminds me, terminals I/O is typically going to be line buffered, not block so most likely every println will result in a system call further slowing down the terminal case. – penguin359 Feb 02 '17 at 04:44
33

Since they are declared as int, once it reaches the max value, the loop will break as the x value will becomes negative.

But when the System.out.println is added to the loop, the speed of execution becomes visible (as outputting to console will slow down the execution speed). However, if you let the 2nd program (the one with syso inside the loop) runs for long enough, it should have the same behavior as the first one (the one without syso inside the loop).

Ace Zachary
  • 439
  • 4
  • 5
13

There can be two reasons for this:

  1. Java optimizes the for loop and since there is no use of x after the loop, simply removes the loop. You can check this by putting System.out.println(x); statement after the loop.

  2. It may be possible that Java is not actually optimizing the loop and it is executing the program correctly and eventually x will grow too large for int and overflow. Integer overflow will most probably make the integer x as negative which will be smaller than j and so it will come out of the loop and print the value of y. This also can be checked by adding System.out.println(x); after the loop.

Also, even in the first case eventually overflow will happen thus rendering it to the second case so it will never be a true endless loop.

4castle
  • 32,613
  • 11
  • 69
  • 106
  • 14
    I choose door number 2. – Robby Cornelissen Feb 01 '17 at 05:00
  • True. It went on the negative scale and exited the loop. But a `sysout` is so slow to add an illusion of an infinite loop. – Pavan Kumar Feb 01 '17 at 05:08
  • 4
    1. Would be a bug. Compiler optimizations are not allowed to change the behavior of a program. If this was an infinite loop, then the compiler may optimize all it wants, however, the result must still be an infinite loop. The real solution is that the OP is mistaken: neither of the two is an infinite loop, one just does more work than the other, so it takes longer. – Jörg W Mittag Feb 01 '17 at 15:09
  • 1
    @JörgWMittag In this case x is a local variable with no relation to anything else. As such it is possible that it's optimized away. But one should look at the bytecode to determine if that is the case, never just assume that the compiler did something like that. – HopefullyHelpful Feb 02 '17 at 09:05
1

They are both not endless loops, initially j = 0 , as long as j < x, j increases(j++), and j is an integer so the loop would run until it reaches the maximum value then overflow(An Integer Overflow is the condition that occurs when the result of an arithmetic operation, such as multiplication or addition, exceeds the maximum size of the integer type used to store it.). for the second example the system just prints the value of y until the loop breaks.

if you are looking for an example of an endless loop, it should look like this

int x = 6;

for (int i = 0; x < 10; i++) {
System.out.println("Still Looping");
}

because (x) would never attain the value of 10;

you could also create an infinite loop with a double for loop:

int i ;

  for (i = 0; i <= 10; i++) {
      for (i = 0; i <= 5; i++){
         System.out.println("Repeat");   
      }
 }

this loop is infinite because the first for loop says i < 10, which is true so it goes into the second for loop and the second for loop increases the value of (i) until it is == 5. Then it proceeds into the first for loop again because i < 10,the process keeps repeating itself because it resets after the second for loop

Kennedy
  • 547
  • 4
  • 23
1

It's a finite loop because once the value of x exceeds 2,147,483,647 (which is the maximum value of an int), x will become negative and not greater than j any more, whether you print y or not.

You can just change the value of y to 100000 and print y in the loop and the loop will break very soon.

The reason why you feel it became infinite is that the System.out.println(y); made the code to be executed very much slower than without any actions.

Joe Cheng
  • 8,804
  • 3
  • 26
  • 25
0

Interesting problem Actually in both cases loop isn't endless

But the main difference between them is when it will terminate and how much time x will take to exceed max int value which is 2,147,483,647 after that it will reach overflow state and the loop will terminate.

Best way to understand this problem is to test a simple example and preserve its results.

Example:

for(int i = 10; i > 0; i++) {}
System.out.println("finished!");

Output:

finished!
BUILD SUCCESSFUL (total time: 0 seconds)

After testing this infinite loop it will take less than 1 second to terminate.

for(int i = 10; i > 0; i++) {
    System.out.println("infinite: " + i);
}
System.out.println("finished!");

Output:

infinite: 314572809
infinite: 314572810
infinite: 314572811
.
.
.
infinite: 2147483644
infinite: 2147483645
infinite: 2147483646
infinite: 2147483647
finished!
BUILD SUCCESSFUL (total time: 486 minutes 25 seconds)

On this test case you will notice a huge difference in the time taken to terminate and finish running the program.

If you aren't patience you will think this loop is endless and won't terminate but in fact it will take hours to terminate and reach the overflow state at i value.

Finally we concluded after we put print statement inside for loop that it will take much more time than loop in the first case without print statement.

That time taken to run the program depends on your computer specifications in particular processing power(processor capacity), operating system and your IDE which is compiling the program.

I test this case on:

Lenovo 2.7 GHz Intel Core i5

OS : Windows 8.1 64x

IDE : NetBeans 8.2

It takes about 8 hours (486 minutes) to finish the program.

Also you can notice that the step increment in the for loop i = i + 1 is very slow factor to reach the max int value.

We can change this factor and make step increment more faster in order to test for loop in less time.

if we put i = i * 10 and test it:

for(int i = 10; i > 0; i*=10) {
           System.out.println("infinite: " + i);
}
     System.out.println("finished!");

Output:

infinite: 100000
infinite: 1000000
infinite: 10000000
infinite: 100000000
infinite: 1000000000
infinite: 1410065408
infinite: 1215752192
finished!
BUILD SUCCESSFUL (total time: 0 seconds)

As you see it's very fast comparing to the previous loop

it takes less than 1 second to terminate and finish running the program.

After this test example I think it should clarify the problem and proves validity of Zbynek Vyskovsky - kvr000's answer, also it will be answer to this question.

Oghli
  • 2,200
  • 1
  • 15
  • 37