Comme @Angew l'a souligné , l' !=opérateur a besoin du même type des deux côtés.
(float)i != ise traduit également par la promotion de la RHS à flotter, c'est ce que nous avons fait (float)i != (float)i.
g ++ génère également une boucle infinie, mais il n'optimise pas le travail de l'intérieur. Vous pouvez voir qu'il convertit int-> float avec cvtsi2sset ucomiss xmm0,xmm0se compare (float)iavec lui-même. (C'était votre premier indice que votre source C ++ ne signifie pas ce que vous pensiez qu'elle faisait, comme l'explique la réponse de @ Angew.)
x != xest seulement vrai quand il est "non ordonné" parce que xc'était NaN. (se INFINITYcompare égal à lui-même en mathématiques IEEE, mais NaN ne le fait pas. NAN == NANest faux, NAN != NANest vrai).
gcc7.4 et les versions antérieures optimisent correctement votre code en jnptant que branche de boucle ( https://godbolt.org/z/fyOhW1 ): continuez à boucler tant que les opérandes x != x ne sont pas NaN. (gcc8 et les versions ultérieures vérifient également jeune rupture de la boucle, échouant à l'optimisation en se basant sur le fait que ce sera toujours vrai pour toute entrée non-NaN). x86 FP compare l'ensemble PF sur non ordonné.
Et BTW, cela signifie que l'optimisation de clang est également sûre : il suffit de CSE (float)i != (implicit conversion to float)icomme étant le même, et de prouver que ce i -> floatn'est jamais NaN pour la plage possible de int.
(Bien que cette boucle atteigne UB de débordement signé, il est autorisé à émettre littéralement n'importe quel asm qu'il veut, y compris une ud2instruction illégale, ou une boucle infinie vide quel que soit le corps de la boucle.) Mais en ignorant le dépassement signé UB , cette optimisation est toujours légale à 100%.
GCC ne parvient pas à optimiser le corps de la boucle, même si le -fwrapvdébordement d'entier signé est bien défini (en tant que complément à 2). https://godbolt.org/z/t9A8t_
Même l'activation -fno-trapping-mathn'aide pas. (La valeur par défaut de GCC est malheureusement d'activer
-ftrapping-mathmême si l'implémentation de GCC est cassée / boguée .) La conversion int-> float peut provoquer une exception FP inexacte (pour des nombres trop grands pour être représentés exactement), donc avec des exceptions éventuellement démasquées, il est raisonnable de ne pas optimisez le corps de la boucle. (Parce que la conversion 16777217en float pourrait avoir un effet secondaire observable si l'exception inexacte est démasquée.)
Mais avec -O3 -fwrapv -fno-trapping-math, c'est une optimisation ratée à 100% de ne pas compiler cela en une boucle infinie vide. Sans #pragma STDC FENV_ACCESS ON, l'état des indicateurs persistants qui enregistrent les exceptions FP masquées n'est pas un effet secondaire observable du code. Non int-> la floatconversion peut entraîner NaN, donc x != xcela ne peut pas être vrai.
Ces compilateurs sont tous optimisés pour les implémentations C ++ qui utilisent IEEE 754 simple précision (binary32) floatet 32 bits int.
La boucle corrigée d'(int)(float)i != i un bogue aurait UB sur les implémentations C ++ avec 16 bits étroit intet / ou plus large float, car vous auriez atteint un débordement d'entier signé UB avant d'atteindre le premier entier qui n'était pas exactement représentable en tant que float.
Mais UB sous un ensemble différent de choix définis par l'implémentation n'a pas de conséquences négatives lors de la compilation pour une implémentation comme gcc ou clang avec l'ABI x86-64 System V.
BTW, vous pouvez calculer statiquement le résultat de cette boucle à partir de FLT_RADIXet FLT_MANT_DIG, défini dans <climits>. Ou du moins vous pouvez en théorie, si floatcorrespond réellement au modèle d'un flotteur IEEE plutôt qu'à un autre type de représentation en nombre réel comme un Posit / unum.
Je ne sais pas à quel point la norme ISO C ++ résout le floatcomportement et si un format qui n'était pas basé sur des champs d'exposant et de significand à largeur fixe serait conforme aux normes.
Dans les commentaires:
@geza Je serais intéressé d'entendre le nombre résultant!
@nada: c'est 16777216
Êtes-vous en train de prétendre que vous avez cette boucle à imprimer / renvoyer 16777216?
Mise à jour: puisque ce commentaire a été supprimé, je ne pense pas. Il est probable que l'OP ne cite que l' floatavant du premier entier qui ne peut pas être exactement représenté sous forme de 32 bits float. https://en.wikipedia.org/wiki/Single-precision_floating-point_format#Precision_limits_on_integer_values c'est à dire ce qu'ils espéraient vérifier avec ce code bogué.
La version corrigée du bogue afficherait bien sûr 16777217, le premier entier qui n'est pas exactement représentable, plutôt que la valeur avant cela.
(Toutes les valeurs flottantes supérieures sont des entiers exacts, mais ce sont des multiples de 2, puis 4, puis 8, etc. pour les valeurs d'exposant supérieures à la largeur du significand. De nombreuses valeurs entières plus élevées peuvent être représentées, mais 1 unité à la dernière place (du significande) est supérieur à 1, donc ce ne sont pas des entiers contigus. Le plus grand fini floatest juste en dessous de 2 ^ 128, ce qui est trop grand pour pair int64_t.)
Si un compilateur sortait de la boucle d'origine et l'affichait, ce serait un bogue du compilateur.