Comment peut "while (i == i);" être une boucle non infinie dans une seule application threadée?

141

J'ai juste une question à laquelle je ne peux pas répondre.

Supposons que vous ayez cette définition de boucle en Java:

while (i == i) ;

Quel est le type iet la valeur de isi la boucle n'est pas une boucle infinie et que le programme n'utilise qu'un seul thread ?

Zizzencs
la source
7
oh pour l'amour de Dieu, les gens auraient-ils l'honneur de dire pourquoi ils votent contre? c'est étiqueté comme une énigme, quel est le problème ???
user54579
10
Je pense que certaines personnes ne peuvent pas arrêter de voter pour quoi que ce soit.
FerranB
1
Bah, désolé d'avoir causé ceci: / Je voulais vraiment juste la réponse, et je ne pouvais pas la résoudre moi-même.
Zizzencs le
1
@nickolai, c'est la nature du SO - le vote à la baisse au volant est malheureusement une réalité et l'idée d'exiger un commentaire a été discutée et rejetée. Mais il semble que la «communauté» soit en faveur de cette question après tout.
paxdiablo
1
Une des astuces de ce genre de question est que les gens supposent le type de certains noms de variables, c'est-à-dire que i est un entier, d est un double, s est une chaîne ou un court, ch est un caractère, l est un long, b pour octet ou booléen. Vous devez vous demander quel type est suggéré et ce que cela pourrait être.
Peter Lawrey

Réponses:

125
double i = Double.NaN;

L'API pour Double.equals () précise la réponse: "Double.NaN == Double.NaN a la valeur false". Ceci est développé dans la spécification du langage Java sous " Types, formats et valeurs à virgule flottante ":

NaNest non ordonnée, de sorte que les opérateurs de comparaison numérique <, <=, >et >= retour falsesi l' une ou les deux opérandes sont NaN. L'opérateur d'égalité ==retourne falsesi l'un des opérandes est NaN, et l'opérateur d'inégalité !=retourne truesi l'un des opérandes est NaN. En particulier, x!=xest truesi et seulement si xestNaN , et (x<y) == !(x>=y)sera falsesi xou yest NaN.

Zach Scrivena
la source
1
OH donc vous POUVEZ faire "Pas un nombre"!
Filip Ekberg
12
son mathématiquement solide, pourquoi un nombre irréel devrait-il en égaler un autre? 5/0! = Sqrt (-4)
Gordon Gustafson
2
@CrazyJugglerDrummer: Peut-être, mais de même x == xdevrait toujours être vrai. Pourquoi quelque chose ne devrait-il pas s'égaler?
Bart van Heukelom
1
Bart: parce que vraiment, inconnu n'est pas toujours égal à inconnu. Parfois, c'est utile, c'est pourquoi les bases de données ont NULL ...
Konerak
2
@inovaovao: non, dans une base de données, null=nullest nul. NULL IS NULLest 1.
Konerak
29

La valeur de iest alors Invalid. "Pas un nombre".

Après quelques recherches sur Google, j'ai découvert que vous POUVEZ avoir NaN (Not a Number) en Java! Ainsi, un nombre à virgule flottante est le type de données et la valeur est NaN. Voir ici

Filip Ekberg
la source
12
Ouais, c'est ça. La valeur de i est Jon Skeet.
Andrew Rollings
Je déteste généralement les gens qui votent sans raison ... J'ai été le premier à dire "Pas un numéro" mais je ne pensais pas que Java pouvait gérer cela, car il ne gère rien d'autre de cool.
Filip Ekberg le
12
double i = Double.NaN;

NaN n'est égal à rien, y compris lui-même.

Bill le lézard
la source
9
float i = Float.NaN;
while(i == i) ;
System.out.println("Not infinite!");
Aaron Maenpaa
la source
8

Je ne suis pas sûr, mais je crois que (i == i) n'est pas une opération atomique dans un processus multithread, donc si la valeur de i sera modifiée par un autre thread entre les poussées de sa valeur à empiler sur le thread exécutant la boucle, alors cette condition peut être faux.

okutane
la source
8

Depuis que d'autres ont dit que c'était NaN, je me suis intéressé à l'implémentation officielle (JDK 6) de Double.isNaN, et voici:

/**
 * 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
la source
2

Considérez Nan comme l'équivalent d'une exception mais utilise une valeur magique dans un calcul. Parce qu'un calcul a échoué - par exemple, racine carrée d'un négatif, diviser par zéro, etc. - cela n'a aucun sens de les comparer à autre chose. Après tout, si diviser par zéro est un nan, est-ce équivalent à la racine carrée de -2 ou à la racine carrée de -3?

Nan permet un calcul qui comprend une étape qui renvoie une réponse invalide à terminer sans introduire d'exceptions supplémentaires. Pour vérifier que la réponse est la valeur, testez simplement la non-nandness (c'est un mot sinon je le sache) via Float.isNan () o équivalent.

mP.
la source
3
Ce serait plus facile de penser à cela comme saufi, mais si je savais vraiment ce qu'était un saufi :-).
paxdiablo
2

J'ajouterais

float i = Float.NaN;

aussi bien que

double i = Double.NaN;

Une astuce courante dans ce genre de questions consiste à supposer que i est un entier. D'autres hypothèses courantes pourraient être que s est une chaîne, x, y sont un double, ch est un caractère, b est un octet, etc. Si vous voyez une question comme celle-ci, vous pouvez parier que «i» n'est pas son type attendu.

Une question similaire est; Cela ne boucle jamais, qu'est-ce que 'x'

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

Une autre question que j'aime bien est; Cette boucle est une boucle infinie, quelles sont les valeurs possibles de x. (: J'en compte douze :)

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

Je sais que c'est une question Java, mais considérer la question pour d'autres langages est intrigant.

En C, un type simple tel que 'int' pourrait présenter un comportement 'se terminer avant que l'univers ne devienne froid' si 'i' était déclaré comme volatile (le compilateur serait donc forcé de faire deux lectures de 'i' pour chaque itération) et si «i» était réellement en mémoire où quelque chose d'autre pourrait l'affecter. Ensuite, la boucle se terminait lorsque «i» changeait entre les deux lectures d'une seule itération. ( Ajouté : un endroit possible - dans un micro-ordinateur où 'i' est en fait situé à l'adresse d'un port d'E / S, peut-être connecté à un capteur de position. Ce serait plus plausible si 'i' était une variable de pointeur ( un pointeur vers la mémoire volatile) et l'instruction était ' while (*i == *i);'.)

Comme en témoignent d'autres réponses, en C ++, l'opérateur '==' peut être fourni par l'utilisateur si i est d'une classe définie par l'utilisateur, donc tout est possible.

Un peu comme NaN, dans un langage basé sur SQL, la boucle ne serait pas infinie si la valeur de i était NULL; cependant, toute valeur non NULL rendrait la boucle infinie. C'est un peu comme Java, où n'importe quel nombre (par opposition à NaN) rend la boucle infinie.

Je ne vois pas que la construction ait une utilité pratique, mais c'est une question triviale intéressante.

Jonathan Leffler
la source
0

J'ai été surpris de ne pas voir cette solution:

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

En réponse à un commentaire, essayez d'exécuter ceci:

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

x devrait toujours égaler l'arc sinus (sin (x)) en théorie, mais en pratique ce n'est pas le cas.

jkeys
la source
3
Pourquoi pas? Vous faites exactement le même calcul sur exactement les mêmes données, les deux résultats contiendront exactement la même erreur.
Loren Pechtel
Je ne me souviens pas exactement où je l'ai lu, mais en fonction de votre machine / implémentation, le sin (x) sur un appel de fonction a une chance minuscule d'égaler sin (x) d'un appel différent. Cela a à voir avec la précision des chiffres à virgule flottante (quelque chose est spécial dans les fonctions trigonométriques qui les empêche de retourner la même valeur deux fois).
jkeys
Voir ma mise à jour. Arcsine "annule" le péché de x, donc ils devraient être égaux, mais ils ne le sont pas.
jkeys
Ce n'est pas la même chose. Comme Loren l'a dit, vous effectuez exactement la même opération sur x, ce qui devrait donner exactement le même résultat avec exactement la même inexactitude. Arcsin ne peut pas inverser le résultat d'un péché avec des nombres à virgule flottante car la valeur transmise asin()ne sera pas exactement précise. Par conséquent, le résultat de asin()sera inexact, rendant x == asin(sin(x))faux. De plus, arcsin n'annule pas nécessairement une opération sin - la fonction sin peut donner le même résultat pour plusieurs valeurs de x, c'est pourquoi asin()ne renvoie que des nombres compris entre -π / 2 et π / 2.
hbw
2
En d'autres termes, arcsin ne peut pas toujours «annuler» la fonction sin car il est impossible pour arcsin de savoir quel était l'angle d'origine. Par exemple, le péché de π / 2 et 5π / 2 donne 1. Mais qu'est-ce que arcsin (1)? Il ne peut évidemment pas rendre les deux, non? Par conséquent, le résultat d'arcsin doit être limité à une plage de 2π radians, ce qui signifie qu'il ne peut pas réellement annuler le résultat de la fonction sin à moins que l'angle d'origine soit compris entre 0 et 2π, ou dans le cas de C, -π / 2 et π / 2. (En effet, arcsin (sin (5π / 2)) = π / 2.) Quoi qu'il en soit, c'était une très longue explication, mais j'espère que cela aidera à éclaircir les idées fausses.
hbw
0

Pas une boucle infinie, un fil :)

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);
    }
}
Andreï
la source
-1

i == in'est pas atomique. Prouvé par un tel programme:

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

Mise à jour Voici un autre exemple de boucle non infinie (aucun nouveau thread n'est créé).

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
la source
@Charles Goodwin Ce que vous dites sur le fait qu'il n'y a pas de possibilité d'écrire un programme Java en utilisant un thread :), donc toutes les autres solutions utilisent au moins deux threads (exactement le même que mon deuxième programme dans la section 'Update').
Andrey
Cela ne freine pas la boucle. Tout code après while (i == i);ne s'exécutera jamais.
aalku
Finalize utilise un autre thread, mais c'est une bonne chose à signaler. C'est la raison pour laquelle la question originale disait "et le programme n'utilise qu'un seul fil" ... en quelque sorte en déclarant explicitement que c'est la réponse évidente et ils souhaitent que vous regardiez plus loin.
Bill K