Как можно «while (i == i);» быть небесконечным циклом в однопоточном приложении?

141

У меня есть вопрос, на который я не могу ответить.

Предположим, у вас есть это определение цикла в Java:

while (i == i) ;

Каков тип iи значение, iесли цикл не является бесконечным, а программа использует только один поток ?

Zizzencs
источник
7
о, ради бога, будут ли люди иметь честь прокомментировать, почему они голосуют против? это помечено как загадка, в чем проблема ???
user54579
10
Я думаю, что некоторые люди не могут перестать голосовать ни за что.
FerranB
1
Ба, извините за причину этого: / Я действительно просто хотел получить ответ, и я не мог решить его для себя.
Zizzencs,
1
@nickolai, такова природа SO - к сожалению, отрицательное голосование стало реальностью, и идея запроса комментария была обсуждена и отвергнута. Но, похоже, «сообщество» все-таки поддерживает этот вопрос.
paxdiablo
1
Один из приемов такого рода вопросов заключается в том, что люди предполагают тип определенных имен переменных, то есть i - int, d - двойное, s - строка или короткое, ch - char, l - длинное, b для байт или логическое значение. Вы должны спросить себя, какой тип предлагается и что это может быть.
Питер Лоури

Ответы:

125
double i = Double.NaN;

API для Double.equals () дает ответ: «Double.NaN == Double.NaN имеет значение false». Это подробно описано в Спецификации языка Java в разделе « Типы, форматы и значения с плавающей запятой »:

NaNявляется неупорядоченным, поэтому численные операторы сравнения <, <=, >и >= возврат , falseесли один или оба операнда NaN. Оператор равенства ==возвращает значение, falseесли один из операндов равен NaN, а оператор неравенства !=возвращает значение, trueесли какой-либо из операндов равен NaN. В частности, x!=xесть, trueесли и только если xестьNaN , и (x<y) == !(x>=y)будет, falseесли xили yесть NaN.

Зак Скривена
источник
1
О, так что вы МОЖЕТЕ сделать "Не число"!
Филип Экберг,
12
это математически твердо, почему одно нереальное число должно равняться другому? 5/0! = Sqrt (-4)
Гордон Густафсон,
2
@CrazyJugglerDrummer: Возможно, но то же самое x == xвсегда должно быть правдой. Почему ничто не должно равняться самому себе?
Bart van Heukelom
1
Барт: потому что на самом деле неизвестное не всегда равно неизвестному. Иногда это полезно, поэтому базы данных имеют NULL ...
Konerak
2
@inovaovao: нет, в БД null=null- null. NULL IS NULLсоставляет 1.
Konerak
29

Тогда значение iнедопустимо. «Не число».

После некоторого поиска в Google я обнаружил, что в Java МОЖЕТ быть NaN (не число)! Итак, число с плавающей точкой - это тип данных, а значение - NaN. Смотрите здесь

Филип Экберг
источник
12
Ага, вот и все. Значение i - это Джон Скит.
Эндрю Роллингс,
Я вообще ненавижу людей, которые голосуют против без всякой причины ... Я был первым, кто сказал «Не число», но не думал, что java может справиться с этим, поскольку он не справляется ни с чем другим классным.
Филип Экберг,
12
double i = Double.NaN;

NaN не равен чему-либо, в том числе самому себе.

Билл Ящерица
источник
9
float i = Float.NaN;
while(i == i) ;
System.out.println("Not infinite!");
Аарон Маенпаа
источник
8

Я не уверен, но я считаю, что (i == i) не является атомарной операцией в многопоточном процессе, поэтому, если значение i будет изменено другим потоком между толчками его значения в стек в потоке, выполняющем цикл, тогда это условие может быть ложным.

окутан
источник
8

Поскольку другие говорили, что это NaN, мне стало любопытно официальная (JDK 6) реализация Double.isNaN, и вот:

/**
 * Returns <code>true</code> if the specified number is a
 * Not-a-Number (NaN) value, <code>false</code> otherwise.
 *
 * @param   v   the value to be tested.
 * @return  <code>true</code> if the value of the argument is NaN;
 *          <code>false</code> otherwise.
 */
static public boolean isNaN(double v) {
    return (v != v);
}
Барт ван Хёкелом
источник
2

Думайте о Nan как о эквиваленте исключения, но при вычислении использует магическое значение. Поскольку расчет не удался - например, квадратный корень из отрицательного числа, разделение на ноль и т.д. - нет смысла сравнивать их с чем-либо еще. В конце концов, если разделить на ноль - это нана, то это эквивалентно квадратному корню из -2 или квадратному корню из -3?

Nan допускает вычисление, которое включает шаг, возвращающий неверный ответ, для завершения без введения дополнительных исключений. Чтобы убедиться, что ответ - это значение, просто проверьте его на ненадежность (это слово, если я не упаковываю его) через эквивалент Float.isNan ().

mP.
источник
3
Было бы легче думать об этом как об исключении, но если бы я действительно знал, что такое исключение :-).
paxdiablo
2

Я бы добавил

float i = Float.NaN;

также как и

double i = Double.NaN;

Обычный трюк в подобных вопросах - это предположение, что i - это int. Другими распространенными предположениями могут быть s - это String, x, y - двойные, ch - char, b - байтовые и т. Д. Если вы видите такой вопрос, вы можете поспорить, что «i» не является ожидаемым типом.

Аналогичный вопрос; Это никогда не зацикливается, что такое 'x'

while(x == x && x != x + 0) { }

Еще один вопрос, который мне очень нравится: Этот цикл представляет собой бесконечный цикл, каковы возможные значения x. (: Я насчитал их двенадцать :)

while(x != 0 && x == -x) { }
Питер Лоури
источник
0

Я знаю, что это вопрос Java, но рассмотрение вопроса для других языков интригует.

В C простой тип, такой как 'int', может демонстрировать поведение 'завершиться до того, как вселенная остынет', если 'i' был объявлен как изменчивый (поэтому компилятор был бы вынужден выполнять два чтения 'i' для каждой итерации) и если «я» действительно было в памяти, на него могло повлиять что-то еще. Затем цикл завершился бы, когда «i» изменилось между двумя чтениями одной итерации. ( Добавлено : возможное место - в микрокомпьютере, где 'i' фактически находится по адресу порта ввода-вывода, возможно, подключенного к датчику положения. Было бы более правдоподобно, если бы 'i' было переменной-указателем ( указатель на энергозависимую память), а оператор был ' while (*i == *i);'.)

Как свидетельствуют другие ответы, в С ++ оператор '==' может быть предоставлен пользователем, если я принадлежит к пользовательскому классу, поэтому все может быть возможным.

Скорее, как NaN, в языке, основанном на SQL, цикл не был бы бесконечным, если бы значение i было NULL; однако любое значение, отличное от NULL, сделает цикл бесконечным. Это больше похоже на Java, где любое число (в отличие от NaN) делает цикл бесконечным.

Я не вижу практического применения этой конструкции, но это интересный вопрос, связанный с пустяками.

Джонатан Леффлер
источник
0

Я был удивлен, не увидев этого решения:

while (sin(x) == sin(x)) //probably won't eval to true

В ответ на комментарий попробуйте запустить это:

double x = 10.5f;
assert (x == asin(sin(x)));

Теоретически x всегда должен равняться арксинусу (sin (x)), но на практике это не так.

jkeys
источник
3
Почему нет? Вы выполняете точно такой же расчет на одних и тех же данных, оба результата будут содержать одну и ту же ошибку.
Loren Pechtel
Я не могу точно вспомнить, где я это читал, но в зависимости от вашей машины / реализации, sin (x) при одном вызове функции имеет минимальный шанс сравняться с sin (x) другого вызова. Это связано с точностью цифр с плавающей запятой (что-то особенное в функциях триггеров, из-за которых они не возвращают одно и то же значение дважды).
jkeys
Смотрите мое обновление. Арксинус «отменяет» грех x, поэтому они должны быть равны, но это не так.
jkeys
Это не одно и то же. Как сказала Лорен, вы выполняете ту же самую операцию x, которая должна дать точно такой же результат с той же неточностью. Arcsin не может отменить результат греха с числами с плавающей запятой, потому что переданное значение asin()не будет точно точным. Следовательно, результат asin()будет неточным, сделав x == asin(sin(x))ложным. Кроме того, arcsin не обязательно «отменяет» операцию sin - функция sin может дать тот же результат для нескольких значений x, поэтому asin()возвращает только числа от -π / 2 до π / 2.
hbw
2
Другими словами, arcsin не всегда может «отменить» функцию sin, потому что arcsin не может знать, каким был исходный угол. Например, грех как π / 2, так и 5π / 2 дает 1. Но что такое arcsin (1)? Очевидно, он не может вернуть оба, верно? Следовательно, результат arcsin должен быть ограничен диапазоном 2π радиан, что означает, что он не может фактически отменить результат функции sin, если исходный угол не находится между 0 и 2π, или, в случае C, -π / 2 и π / 2. (Действительно, arcsin (sin (5π / 2)) = π / 2.) В любом случае, это было действительно длинное объяснение, но я надеюсь, что это поможет прояснить любые заблуждения.
hbw
0

Не бесконечный цикл, один поток :)

import static B.*;
public class A {
    public static void main(String[] args) {
        System.out.println("Still Running");
        while (i == i) ;
    }
}


public class B {

    public static int i;
    static {
        System.exit(0);
    }
}
Андрей
источник
-1

i == iне атомарен. Доказано такой программой:

static volatile boolean i = true;
public static void main(String[] args) throws InterruptedException
{
    new Thread() {
        @Override
        public void run() {
            while (true) {
                i = !i;
            }
        }
    }.start();

    while (i == i) ;
    System.out.println("Not atomic! i: " + i);
}

Обновление Вот еще один пример небесконечного цикла (новые потоки не создаются).

public class NoNewThreads {
    public static void main(String[] args) {
        new NoNewThreads();
        System.gc();
        int i = 500;
        System.out.println("Still Running");
        while (i == i) ;
    }

    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        Thread.sleep(1000);
        System.exit(0);
    }
}
Андрей
источник
@Charles Goodwin То, что вы говорите о том, что нет возможности написать программу на Java с использованием одного потока :), поэтому все другие решения используют как минимум два потока (точно так же, как моя вторая программа в разделе «Обновление»).
Андрей
Это не тормозит петлю. Любой код после while (i == i);никогда не будет выполнен.
aalku
Finalize использует другой поток, но стоит отметить его. Это причина, по которой в исходном вопросе говорилось: «а программа использует только один поток» ... как бы явно заявляя, что это очевидный ответ, и они хотят, чтобы вы посмотрели дальше.
Bill K