Wie kann "while (i == i);" eine nicht unendliche Schleife in einer Single-Threaded-Anwendung sein?

141

Ich habe gerade eine Frage, die ich nicht beantworten kann.

Angenommen, Sie haben diese Schleifendefinition in Java:

while (i == i) ;

Was ist der Typ iund der Wert von, iwenn die Schleife keine Endlosschleife ist und das Programm nur einen Thread verwendet ?

Zizzencs
quelle
7
Oh, um Gottes willen, hätten die Menschen die Ehre zu kommentieren, warum sie abstimmen? Dies ist als Rätsel markiert, was ist das Problem?
user54579
10
Ich denke, einige Leute können nicht aufhören, für irgendetwas zu stimmen.
FerranB
1
Bah, tut mir leid, dass ich das verursacht habe: / Ich wollte wirklich nur die Antwort und konnte sie nicht für mich selbst lösen.
Zizzencs
1
@nickolai, das ist die Natur von SO - Drive-by-Downvoting ist leider Realität und die Idee, einen Kommentar zu verlangen, wurde diskutiert und abgelehnt. Aber es sieht so aus, als ob die "Community" diese Frage doch befürwortet.
Paxdiablo
1
Einer der Tricks dieser Art von Frage ist, dass die Leute den Typ bestimmter Variablennamen annehmen, dh i ist ein int, d ist ein Double, s ist eine Zeichenfolge oder eine kurze, ch ist ein Zeichen, l ist eine lange, b für Byte oder Boolescher Wert. Sie müssen sich fragen, welcher Typ vorgeschlagen wird und welcher sein könnte.
Peter Lawrey

Antworten:

125
double i = Double.NaN;

Die API für Double.equals () gibt die Antwort aus: "Double.NaN == Double.NaN hat den Wert false". Dies wird in der Java-Sprachspezifikation unter " Gleitkommatypen, -formate und -werte " erläutert :

NaNist ungeordnet, so dass die numerischen Vergleichsoperatoren <, <=, >, und >= Rückkehr , falsewenn eine oder beide Operanden NaN. Der Gleichheitsoperator gibt ==zurück, falsewenn einer der Operanden ist NaN, und der Ungleichheitsoperator gibt !=zurück, truewenn einer der Operanden ist NaN. Insbesondere x!=xist truegenau dannxNaN , wenn es ist und sein (x<y) == !(x>=y)wird, falsewenn xoder yist NaN.

Zach Scrivena
quelle
1
OH, also kannst du "Not a Number" machen!
Filip Ekberg
12
es ist mathematisch solide, warum sollte eine unwirkliche Zahl gleich einer anderen sein? 5/0! = Sqrt (-4)
Gordon Gustafson
2
@CrazyJugglerDrummer: Vielleicht, sollte aber auch x == ximmer wahr sein. Warum sollte sich etwas nicht gleichstellen?
Bart van Heukelom
1
Bart: Weil wirklich unbekannt nicht immer gleich unbekannt ist. Manchmal ist es nützlich, deshalb haben Datenbanken NULL ...
Konerak
2
@inovaovao: Nein, in einer Datenbank null=nullist null. NULL IS NULList 1.
Konerak
29

Der Wert von iist dann ungültig. "Keine Zahl".

Nach einigem googeln fand ich heraus, dass Sie NaN (Not a Number) in Java haben können! Eine Gleitkommazahl ist also der Datentyp und der Wert ist NaN. Siehe hier

Filip Ekberg
quelle
12
Ja, das ist es. Der Wert von i ist Jon Skeet.
Andrew Rollings
Ich hasse im Allgemeinen Leute, die ohne Grund abstimmen ... Ich war der erste, der "Not a Number" sagte, aber ich dachte nicht, dass Java damit umgehen könnte, da es mit nichts anderem Coolem umgehen kann.
Filip Ekberg
12
double i = Double.NaN;

NaN ist nichts gleich, auch nicht sich selbst.

Bill die Eidechse
quelle
9
float i = Float.NaN;
while(i == i) ;
System.out.println("Not infinite!");
Aaron Maenpaa
quelle
8

Ich bin nicht sicher, aber ich glaube, dass (i == i) keine atomare Operation in einem Multithread-Prozess ist. Wenn also der i-Wert zwischen den Pushs seines Werts geändert wird, um ihn auf dem Thread zu stapeln, der die Schleife ausführt, kann diese Bedingung sei falsch.

okutane
quelle
8

Da andere sagten, es sei NaN, wurde ich neugierig auf die offizielle (JDK 6) Implementierung von Double.isNaNund siehe:

/**
 * 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);
}
Bart van Heukelom
quelle
2

Stellen Sie sich Nan als Äquivalent einer Ausnahme vor, verwenden Sie jedoch einen magischen Wert innerhalb einer Berechnung. Da eine Berechnung fehlgeschlagen ist - z. B. Quadratwurzel eines Negativs, Teilen durch Null usw. - macht es keinen Sinn, sie mit irgendetwas anderem zu vergleichen. Wenn die Division durch Null ein Nan ist, entspricht sie schließlich der Quadratwurzel von -2 oder der Quadratwurzel von -3?

Nan ermöglicht eine Berechnung, die einen Schritt enthält, der eine ungültige Antwort zurückgibt, ohne zusätzliche Ausnahmen einzuführen. Um zu überprüfen, ob die Antwort ein Wert ist, testen Sie einfach über Float.isNan () o Äquivalent auf Nicht-Nandness (ist das Wort, wenn ich es nicht einsacke).

mP.
quelle
3
Es wäre einfacher, sich das als Exceptiolbut vorzustellen, wenn ich tatsächlich wüsste, was ein Exceptiolbut ist :-).
Paxdiablo
2

Ich würde hinzufügen

float i = Float.NaN;

ebenso gut wie

double i = Double.NaN;

Ein häufiger Trick bei solchen Fragen ist die Annahme, dass ich ein Int bin. Andere gängige Annahmen könnten sein: s ist ein String, x, y sind ein Double, ch ist ein Zeichen, b ist ein Byte usw. Wenn Sie eine Frage wie diese sehen, können Sie darauf wetten, dass 'i' nicht der erwartete Typ ist.

Eine ähnliche Frage ist; Dies schleift nie, was ist 'x'

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

Eine andere Frage, die ich sehr mag, ist: Diese Schleife ist eine Endlosschleife. Was sind die möglichen Werte von x? (: Ich zähle zwölf davon :)

while(x != 0 && x == -x) { }
Peter Lawrey
quelle
0

Ich weiß, dass dies eine Java-Frage ist, aber die Frage für andere Sprachen in Betracht zu ziehen, ist faszinierend.

In C könnte ein einfacher Typ wie 'int' das Verhalten 'Beenden, bevor das Universum kalt wird' aufweisen, wenn 'i' als flüchtig deklariert wird (sodass der Compiler gezwungen wäre, für jede Iteration zwei Lesevorgänge von 'i' durchzuführen). und wenn 'ich' tatsächlich in Erinnerung wäre, wo etwas anderes es beeinflussen könnte. Dann würde die Schleife enden, wenn 'i' zwischen den beiden Lesevorgängen einer einzelnen Iteration wechselt. ( Hinzugefügt : ein möglicher Ort - in einem Mikrocomputer, an dem sich 'i' tatsächlich an der Adresse eines E / A-Ports befindet, möglicherweise verbunden mit einem Positionssensor. Es wäre plausibler, wenn 'i' eine Zeigervariable wäre ( ein Zeiger auf flüchtigen Speicher) und die Anweisung war ' while (*i == *i);'.)

Wie aus anderen Antworten hervorgeht, kann in C ++ der Operator '==' vom Benutzer angegeben werden, wenn ich einer benutzerdefinierten Klasse angehöre, sodass möglicherweise alles möglich ist.

Ähnlich wie bei NaN wäre in einer SQL-basierten Sprache die Schleife nicht unendlich, wenn der Wert von i NULL wäre. Jeder Nicht-NULL-Wert würde die Schleife jedoch unendlich machen. Dies ist eher wie bei Java, wo eine beliebige Zahl (im Gegensatz zu NaN) die Schleife unendlich macht.

Ich sehe keine praktische Verwendung des Konstrukts, aber es ist eine interessante Trivia-Frage.

Jonathan Leffler
quelle
0

Ich war überrascht, diese Lösung nicht zu sehen:

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

Versuchen Sie als Antwort auf einen Kommentar Folgendes auszuführen:

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

x sollte theoretisch immer gleich dem Arkussinus (sin (x)) sein, in der Praxis jedoch nicht.

jkeys
quelle
3
Warum nicht? Sie führen genau die gleiche Berechnung mit genau den gleichen Daten durch. Beide Ergebnisse enthalten genau den gleichen Fehler.
Loren Pechtel
Ich kann mich nicht genau erinnern, wo ich es gelesen habe, aber abhängig von Ihrer Maschine / Implementierung hat die Sünde (x) bei einem Funktionsaufruf eine winzige Chance, der Sünde (x) eines anderen Aufrufs zu entsprechen. Dies hat mit der Genauigkeit von Gleitkommazahlen zu tun (etwas Besonderes an den Triggerfunktionen ist, dass sie nicht zweimal denselben Wert zurückgeben).
Schlüssel 17.
Siehe mein Update. Arcsine "macht" die Sünde von x rückgängig, daher sollten sie gleich sein, aber sie sind es nicht.
Schlüssel 17.
Das ist nicht dasselbe. Wie Loren sagte, führen Sie genau die gleiche Operation aus x, die genau das gleiche Ergebnis mit genau der gleichen Ungenauigkeit liefern sollte. Arcsin kann das Ergebnis einer Sünde nicht mit Gleitkommazahlen umkehren, da der übergebene Wert asin()nicht genau ist. Daher ist das Ergebnis von asin()ungenau und macht x == asin(sin(x))falsch. Außerdem "macht" arcsin eine sin-Operation nicht unbedingt "rückgängig" - die sin-Funktion kann für mehrere Werte von x das gleiche Ergebnis liefern, weshalb asin()nur Zahlen zwischen -π / 2 und π / 2 zurückgegeben werden.
Hbw
2
Mit anderen Worten, Arcsin kann die Sin-Funktion nicht immer "rückgängig machen", da Arcsin nicht wissen kann, wie der ursprüngliche Winkel war. Zum Beispiel ergibt die Sünde von π / 2 und 5π / 2 1. Aber was ist Arcsin (1)? Es kann offensichtlich nicht beides zurückgeben, oder? Daher muss das Ergebnis von Arcsin auf einen Bereich von 2π Radiant beschränkt werden, was bedeutet, dass es das Ergebnis der Sinusfunktion nur dann rückgängig machen kann, wenn der ursprüngliche Winkel zwischen 0 und 2π liegt, oder im Fall von C -π / 2 und π / 2. (In der Tat ist arcsin (sin (5π / 2)) = π / 2.) Wie auch immer, das war eine wirklich lange Erklärung, aber ich hoffe, das hilft, Missverständnisse auszuräumen.
Hbw
0

Keine Endlosschleife, ein Thread :)

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);
    }
}
Andrey
quelle
-1

i == iist nicht atomar. Von einem solchen Programm bewiesen:

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);
}

Update Hier ist ein weiteres Beispiel für eine Endlosschleife (es werden keine neuen Threads erstellt).

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);
    }
}
Andrey
quelle
@ Charles Goodwin Was Sie zu der Tatsache sagen, dass es keine Möglichkeit gibt, ein Java-Programm mit einem Thread zu schreiben :), also verwenden alle anderen Lösungen mindestens zwei Threads (genau das gleiche wie mein zweites Programm im Abschnitt 'Update').
Andrey
Das bremst die Schleife nicht. Jeder Code wird danach while (i == i);nie mehr ausgeführt.
Aalku
Finalize verwendet einen anderen Thread, aber es ist eine gute Sache, darauf hinzuweisen. Aus diesem Grund lautete die ursprüngliche Frage "und das Programm verwendet nur einen Thread". Es wird ausdrücklich darauf hingewiesen, dass dies die offensichtliche Antwort ist und dass Sie weiter suchen sollen.
Bill K