2010-01-27 5 views
4

La réponse à ce problème de devoirs de l'échantillon est « 1000000 », mais je ne comprends pas pourquoi:Pourquoi cette boucle à virgule flottante se termine-t-elle à 1 000 000?

Quelle est la sortie du code ci-dessous?

int main(void) { 
    float k = 1; 
    while (k != k + 1) { 
    k = k + 1; 
    } 
    printf(“%g”, k); // %g means output a floating point variable in decimal 
} 

Si le programme fonctionne indéfiniment, mais ne produit aucune sortie, écrire que la boucle sans réponse à la question. Tous les programmes se compilent et fonctionnent. Ils peuvent ou peuvent ne pas contenir d'erreurs graves, cependant. Vous devriez supposer que int est quatre octets. Vous devez supposer que float a l'équivalent de six chiffres décimaux de précision. Vous pouvez arrondir votre réponse à la puissance la plus proche de 10 (par exemple, vous pouvez dire 1 000 au lieu de 2 (c'est-à-dire 1024)).

Je ne comprends pas pourquoi la boucle se terminerait jamais.

+3

est-ce un copier-coller d'un devoir ou d'une question de test? –

+3

C'est définitivement les devoirs. –

+0

Voilà les devoirs ... mais de toute façon, "INFINITE LOOP". – aviraldg

Répondre

15

Il ne s'exécute pas éternellement pour la simple raison que les nombres à virgule flottante ne sont pas parfaits.

À un certain point, k deviendra assez grand pour que l'ajout de 1 à lui n'aura aucun effet.

À ce stade, k sera égal à k+1 et votre boucle se terminera.

Les nombres à virgule flottante peuvent être différenciés par une seule unité uniquement lorsqu'ils se trouvent dans une certaine plage.

À titre d'exemple, disons que vous avez un type entier avec 3 décimales de précision pour un entier positif et un exposant unique chiffres décimaux.

Avec cela, vous pouvez représenter les nombres de 0 à 999 parfaitement que 000x10 par 999x10 (depuis 10 est 1):

Qu'est-ce qui se passe quand vous voulez représenter 1000? Vous devez utiliser 100x10 . Ceci est encore parfaitement représenté.

Cependant, il n'y a pas de façon précise pour représenter 1001 avec ce schéma, le prochain numéro, vous pouvez représenter est 101x10 qui est 1010.

Ainsi, lorsque vous ajoutez 1 à 1000, vous Obtenir la correspondance la plus proche qui est 1000.

6

Le code utilise une variable float.

Comme spécifié dans la question, float a 6 chiffres de précision, ce qui signifie que tous les chiffres après le sixième seront inexacts. Par conséquent, une fois que vous passez un million, le dernier chiffre sera inexact, de sorte que l'incrémentation ne peut avoir aucun effet.

+0

Je vous suggère d'exécuter le code OP et de voir que vous avez complètement tort. Sur les implémentations typiques, le résultat sera d'environ 16 millions (ou il pourrait ne pas s'arrêter du tout). – gnasher729

4

La sortie de ce programme est pas spécifiée par la norme C, étant donné que la sémantique du type float ne sont pas spécifiés.Un résultat probable (ce que vous obtiendrez sur une plate-forme pour laquelle l'arithmétique float est évaluée en IEEE-754 simple précision) est 2^24.

Tous les entiers inférieurs à 2^24 sont exactement représentables en simple précision, donc le calcul ne s'arrêtera pas avant ce point. Le nombre représentatif suivant simple après 2^24, cependant, est 2^24 + 2. Puisque 2^24 + 1 est exactement à mi-chemin entre ce nombre et 2^24, dans le mode d'arrondi IEEE-754 par défaut il arrondit à celui dont le bit de fin est zéro, qui est 2^24. Les autres réponses possibles sont 2^53 et 2^64. D'autres réponses sont possibles. Infinity (la valeur à virgule flottante) pourrait résulter d'une plate-forme pour laquelle le mode d'arrondi par défaut est arrondi, par exemple. Comme d'autres l'ont noté, une boucle infinie est également possible sur les plates-formes qui évaluent les expressions à virgule flottante dans un type plus large (qui est la source de toutes sortes de confusion des programmeurs, mais permise par la norme C).

3

En fait, sur la plupart des compilateurs C, ceci fonctionnera pour toujours (boucle infinie), bien que le comportement précis soit défini par l'implémentation.

La raison pour laquelle la plupart des compilateurs donneront une boucle infinie est qu'ils évaluent toutes les expressions à virgule flottante à doubles précision et que les valeurs ronde à flotteur (simple) de précision lors de l'enregistrement dans une variable. Ainsi, lorsque la valeur de k atteint environ 2^24, k == k + 1 sera toujours évaluée comme fausse (car un double peut contenir la valeur k + 1 sans arrondir), mais l'affectation k = k + 1 sera un noop, car k + 1 doit être arrondie pour tenir dans un flotteur

modifier

gcc sur x86 obtient ce comportement en boucle infinie. Fait intéressant sur x64, il ne le fait pas, car il utilise des instructions sse qui font la comparaison dans la précision du flotteur.

+0

Quels compilateurs C avez-vous essayé? J'ai essayé sur GCC et il s'est arrêté presque instantanément. – Amok

+0

même si vous utilisez le double, il s'arrêtera à environ 1e16-1e17. Parce que le point flottant est toujours fini. Les flotteurs ont 6-7 chiffres de précision, donc il s'arrête à 1e6. De même pour les doubles qui ont 16-17 chiffres de précision. S'il y a une différence, c'est parce que vous utilisez différentes options de compilation pour les calculs à virgule flottante. –

+1

@ LưuVĩnhPhúc: Non, cela ne s'arrêtera pas. La comparaison est effectuée en double précision, mais l'addition utilise float. Donc, une fois que le flotteur atteint 2^24, il n'augmente plus, mais la comparaison n'échoue jamais. Si vous utilisiez double pour la variable, il s'arrêterait après un temps très long (à moins que vous ayez un compilateur extrêmement optimisant qui définirait simplement le résultat à 2^53). – gnasher729

Questions connexes