Для какого значения я делает цикл while (i == я + 1) {} навсегда?

Я столкнулся с этой загадкой из углубленного курса программирования на экзамене в университете Великобритании.

Рассмотрим следующий цикл, в котором я пока не объявлен:

while (i == i + 1) {}

Найдите определение i, которое предшествует этому циклу, так, чтобы цикл while продолжался вечно.

Следующий вопрос, который задал тот же вопрос для этого фрагмента кода:

while (i != i) {}

было очевидно для меня. Конечно, в этой другой ситуации это NaN но я действительно застрял на предыдущей. Это связано с переполнением? Что заставило бы такой цикл зацикливаться навсегда в Java?

Ответ 1

Во-первых, поскольку цикл while (i == я + 1) {} не меняет значение i, создание бесконечного цикла эквивалентно выбору значения i, удовлетворяющего i == я + 1.

Есть много таких значений:

Начнем с "экзотических":

double i = Double.POSITIVE_INFINITY;

или же

double i =  Double.NEGATIVE_INFINITY;

Причина этих значений, удовлетворяющих i == я + 1, указана в
JLS 15.18.2. Аддитивные операторы (+ и -) для числовых типов:

Сумма бесконечности и конечного значения равна бесконечному операнду.

Это неудивительно, поскольку добавление конечного значения к бесконечному значению должно привести к бесконечному значению.

Тем не менее, большинство значений i которые удовлетворяют i == я + 1 являются просто большими double (или float) значениями:

Например:

double i = Double.MAX_VALUE;

или же

double i = 1000000000000000000.0;

или же

float i = 1000000000000000000.0f;

Типы double и float имеют ограниченную точность, поэтому, если вы берете достаточно большое значение типа double или float, добавление 1 к нему приведет к тому же значению.

Ответ 2

Эти загадки подробно описаны в книге Джошуа Блоха и Нила Гафтера "Головоломки Java: ловушки, ловушки и угловые случаи".

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

или же:

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

оба приведут к бесконечному циклу, потому что добавление 1 к достаточно большому значению с плавающей запятой не изменит значение, потому что это не "ликвидирует разрыв" для его преемника 1.

Примечание о второй головоломке (для будущих читателей):

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

также приводит к бесконечному циклу, потому что NaN не равно ни одному значению с плавающей запятой, включая себя 2.


1 - Java Puzzlers: ловушки, ловушки и угловые случаи (глава 4 - Loopy Puzzlers).

2 - JLS §15.21.1

Ответ 3

Просто идея: как насчет логических значений?

bool i = TRUE;

Разве это не тот случай, когда i + 1 == i?

Ответ 4

double я = Double.POSITIVE_INFINITY;