119

I ran cross this puzzler from an advanced programming course at a UK university exam.

Consider the following loop, in which i is, so far, undeclared:

while (i == i + 1) {}

Find the definition of i, that precedes this loop, such that the while loop continues for ever.

The next question, which asked the same question for this code snippet:

while (i != i) {}

was obvious to me. Of course in this other situation it is NaN but I am really stuck on the prior one. Does this have to do with overflow? What would cause such a loop to loop for ever in Java?

Oleksandr Pyrohov
  • 14,685
  • 6
  • 61
  • 90
jake mckenzie
  • 1,246
  • 2
  • 8
  • 9
  • 3
    Any possibilities to override `.equals()` method? Since i is undeclared, we may use any class of what we want. – Geno Chen Nov 28 '18 at 09:35
  • @jsheeran That's the question, what should `i` be to make it loop infinitely. – Mark Nov 28 '18 at 09:35
  • 2
    @GenoChen: but the code doesn't use `.equals`. And `==` does NOT ever invoke `.equals` – dave_thompson_085 Nov 28 '18 at 09:42
  • @Raedwald I am running an algorithms seminar and I am tasked with finding debugging problems for students. I ran across this problem and became curious. – jake mckenzie Nov 28 '18 at 09:52
  • 7
    @Raedwald studying "unprofessional" code makes you more "professional", so... Anyway, it's a good question – Andrew Tobilko Nov 28 '18 at 09:56
  • In C, int i = INT_MAX; could potentially also loop forever, but in Java overflow is fully defined behaviour and would terminate the loop. – jpa Nov 28 '18 at 11:12
  • 2
    While I see how one can be intrigued by such a question for an instant (for fun), I fail to see how it has a valid place in what is called _"Advanced Programming Exam"_. The same exam states: _"style is significant: you might get more credit for a submission with well-structured code"_. I mean, you've got to be kidding. WTF, professor, WTF? You been smoking weed again? – Damon Nov 28 '18 at 13:02
  • 1
    @Damon I suspect this professor has been subjected to many years of grading code from students who can't spell "structured", much less code with with any structure. This may just be his way of rewarding students who take the time to code clearly. – Andrew Henle Nov 28 '18 at 14:15
  • 12
    Fun fact, in C# this also works for nullable numeric types whose values are `null`, since `null == null` is true, and `null + 1` is `null`. – Eric Lippert Nov 28 '18 at 15:38
  • @Daman - judging by the very large number of questions here on SO which ultimately come down to a failure to understand the properties of "floating point arithmetic" (including not knowing why `0.2 * 5 != 1.0`) I believe that apparently (and sadly) in today's world 1) knowing how "floating point arithmetic" works is "advanced programming" and 2) it is knowledge sorely needed by many many working programmers. – davidbak Nov 28 '18 at 18:13
  • 1
    @davidbak: `0.2 * 5` seems to be exactly `1.0` in C/Java/Python/Ruby. `0.2 + 0.1 != 0.3` seems true for all those languages, though. – Eric Duminil Nov 28 '18 at 19:32
  • 4
    @EricDuminil: The situation is far worse than you imagine. In many languages, floating point arithmetic must be done in *at least* the 64 bits of precision specified by a double, which means that it can be done in *higher precision* at the whim of the compiler, **and in practice this happens**. I can point you at a dozen questions on this site from C# programmers who are wondering why `0.2 + 0.1 == 0.3` changes its value depending on compiler settings, the phase of the moon, and so on. – Eric Lippert Nov 28 '18 at 23:38
  • 5
    @EricDuminil: The blame for this mess falls on Intel, who gave us a chip set that does higher-precision and *faster* floating point arithmetic if the numbers can be enregistered, which means that the results of a floating point computation can change their values depending on how well the register scheduler in the optimizer works today. Your choices as a language designer are then between *repeatable computations* and *fast, precise computations*, and the community that cares about floating point math will opt for the latter. – Eric Lippert Nov 28 '18 at 23:41
  • Possible duplicate of [Endless for loop with float](https://stackoverflow.com/questions/36444413/endless-for-loop-with-float) – Thomas Ayoub Nov 29 '18 at 09:48
  • @ThomasAyoub I don't really see how this is a duplicate of that post. The problem I presented is more general than the quirks of the IEEE 754 standard. That post is also meant for C# and .NET which have their own quirks as opposed to Java as other commentators have mentioned in this post. – jake mckenzie Dec 01 '18 at 08:41

4 Answers4

142

First of all, since the while (i == i + 1) {} loop doesn't change the value of i, making this loop infinite is equivalent to choosing a value of i that satisfies i == i + 1.

There are many such values:

Let's start with the "exotic" ones:

double i = Double.POSITIVE_INFINITY;

or

double i =  Double.NEGATIVE_INFINITY;

The reason for these values satisfying i == i + 1 is stated in
JLS 15.18.2. Additive Operators (+ and -) for Numeric Types:

The sum of an infinity and a finite value is equal to the infinite operand.

This is not surprising, since adding a finite value to an infinite value should result in an infinite value.

That said, most of the values of i that satisfy i == i + 1 are simply large double (or float) values:

For example:

double i = Double.MAX_VALUE;

or

double i = 1000000000000000000.0;

or

float i = 1000000000000000000.0f;

The double and float types have limited precision, so if you take a large enough double or float value, adding 1 to it will result in the same value.

Eran
  • 387,369
  • 54
  • 702
  • 768
  • 10
    Or `(double)(1L<<53)` -- or `float i = (float)(1<<24)` – dave_thompson_085 Nov 28 '18 at 09:43
  • 1
    And `double i = 9007199254740992L;` (or `...94L`, or `...96L`., etc.), since with a `double`, `9007199254740992 + 1` is `9007199254740992`, since floating-point imprecision manifests at the whole number scale for a number that big. – T.J. Crowder Nov 28 '18 at 13:34
  • 1
    (Sorry, there's an entire paragraph mentioning that which I appear to have missed, as I just scanned for the number...) – T.J. Crowder Nov 28 '18 at 13:59
  • "happens to make sense"? I bet it was _designed_ to make sense! – Ruslan Nov 28 '18 at 15:28
  • 3
    @Ruslan: Any mathematician would disagree. Floating point numbers make very little sense. They are non-associative, non-reflexive (NaN != NaN), and not even substitutable (-0 == 0, but 1/0 != 1/-0). So most of the machinery of algebra is inapplicable. – Kevin Nov 28 '18 at 16:35
  • 2
    @Kevin while floating-point numbers can't indeed make too much sense in general, the behavior of infinities (which is what is described in that sentence) was designed to make sense. – Ruslan Nov 28 '18 at 16:38
  • 4
    @Kevin To be fair to floats, if you deal with infinities or undefined values you can't assume the properties you listed in algebra either. – Voo Nov 28 '18 at 17:11
  • 2
    @Kevin: IMHO, floating-point maths could have made a lot more sense if they'd replaced the concepts of "positive and negative zero" sign positive, negative, and unsigned "infinitesimals" along with one "true zero", and made NaN equal to itself. True zero could behave as an additive identity in *all* cases, and something operations involving division by infinitesimals would lose their bias towards assuming the infinitesimals are positive. – supercat Nov 28 '18 at 17:15
  • 1
    @Voo: Substitution and reflexivity are usually regarded as non-negotiable features in most algebras, including those with infinities. This is because, in most mathematics, equality and identity are the same thing (so it does not make sense to have objects which are not equal to themselves, or for the "same" object to behave differently when substituted into the same expression). – Kevin Nov 28 '18 at 18:18
  • 2
    @Kevin I think the problem is, that we're a bit too generic here ("most of mathematics" is waaayy too generic to make any statements of this kind). What kind of set and algebraic structures do you have in mind? I'll go first: The set of integers including positive and negative infinity is **not** a valid ring. That's one of the most basic algebraic structures, so I'm wondering what you have in mind. – Voo Nov 28 '18 at 21:49
  • @Voo: How about first-order logic? That already has reflexivity and (to some degree) substitution. Everything else is built atop logic, so it's *really hard* to find a structure that the floating point numbers don't violate. Unless you decide that "floating point equality" is not actually equality... but then why do we use the equals symbol? – Kevin Nov 29 '18 at 06:46
  • @Kevin First order logic simply requires a number of statements in a specific form which can be combined using boolean logic. So what are your statements that work for the set of reals and infinity that don't work for floats? But even if, since I can trivially create some valid first order logic system for floats your argument that you can't use them in general is clearly wrong (why wouldn't that be the case though? First order logic deals with a completely different problem really) – Voo Nov 29 '18 at 08:00
  • @Kevin What mathematical system are you using to decide on equality? Because `inf = inf` is undefined in any system I can think of from the top of my hat. – Voo Nov 29 '18 at 18:39
  • @Voo: First-order logic already has equality as a primitive. You don't need to introdice "a mathematical system" beyond basic existential and universal quantifiers. – Kevin Nov 29 '18 at 18:40
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/184489/discussion-between-voo-and-kevin). – Voo Nov 29 '18 at 18:50
64

These puzzles are described in detail in the "Java Puzzlers: Traps, Pitfalls, and Corner Cases" book by Joshua Bloch and Neal Gafter.

double i = Double.POSITIVE_INFINITY;
while (i == i + 1) {}

or:

double i = 1.0e40;
while (i == i + 1) {}

both will result in an infinite loop, because adding 1 to a floating-point value that is sufficiently large will not change the value, because it doesn't "bridge the gap" to its successor1.

A note about the second puzzle (for future readers):

double i = Double.NaN;
while (i != i) {}

also results in an infinite loop, because NaN is not equal to any floating-point value, including itself 2.


1 - Java Puzzlers: Traps, Pitfalls, and Corner Cases (chapter 4 - Loopy Puzzlers).

2 - JLS §15.21.1

Oleksandr Pyrohov
  • 14,685
  • 6
  • 61
  • 90
1

double i = Double.POSITIVE_INFINITY;

0

Just an idea: what about booleans?

bool i = TRUE;

Isn't this a case where i + 1 == i?

Dominique
  • 16,450
  • 15
  • 56
  • 112
  • depends on the language. Many languages automatically coerce booleans to ints when combined with an int. Others do as you suggest - coercing the int to a boolean. – Carl Witthoft Nov 29 '18 at 16:12
  • 8
    This question is a Java question, and your suggestion doesn't pass compilation in Java (which has no `+` operator that takes a `boolean` and an `int` as operands). – Eran Nov 30 '18 at 09:05
  • @Eran: that's the whole idea of operator overloading. You can make Java booleans behave like C++ ones. – Dominique Nov 30 '18 at 09:26
  • 4
    Except that Java doesn't support operator overloading, so you can't. – CupawnTae Dec 05 '18 at 09:33