Для какого значения я делает цикл 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;