Je sais que l'utilisation ==
pour vérifier l'égalité des variables à virgule flottante n'est pas un bon moyen. Mais je veux juste savoir cela avec les déclarations suivantes:
float x = ...
float y = x;
assert(y == x)
Puisque y
est copié de x
, l'affirmation sera-t-elle vraie?
c++
floating-point
Wei Li
la source
la source
-m32
), soit en demandant à GCC d'utiliser le x87 FPU (-mfpmath=387
).Réponses:
Outre le
assert(NaN==NaN);
cas souligné par kmdreko, vous pouvez avoir des situations avec x87-math, lorsque des flottants 80 bits sont temporairement stockés en mémoire et plus tard comparés à des valeurs qui sont toujours stockées dans un registre.Exemple minimal possible, qui échoue avec gcc9.2 lorsqu'il est compilé avec
-O2 -m32
:Démo Godbolt: https://godbolt.org/z/X-Xt4R
Le
volatile
peut probablement être omis, si vous parvenez à créer une pression de registre suffisante pour avoiry
stocké et rechargé à partir de la mémoire (mais confondez suffisamment le compilateur, pour ne pas omettre la comparaison).Voir la référence de la FAQ GCC:
la source
float
précision standard à une précision supplémentaire.-ffloat-store
semble être le moyen d'empêcher cela.Ce ne sera pas vrai si
x
c'est le casNaN
, car les comparaisons surNaN
sont toujours fausses (oui, mêmeNaN == NaN
). Pour tous les autres cas (valeurs normales, valeurs sous-normales, infinis, zéros), cette affirmation sera vraie.Les conseils pour éviter les
==
flottants s'appliquent aux calculs car les nombres à virgule flottante sont incapables d'exprimer de nombreux résultats exactement lorsqu'ils sont utilisés dans des expressions arithmétiques. L'affectation n'est pas un calcul et il n'y a aucune raison pour que l'affectation produise une valeur différente de l'original.L'évaluation à précision étendue ne devrait pas poser de problème si la norme est respectée. De
<cfloat>
hérité de C [5.2.4.2.2.8] ( soulignement le mien ):Cependant, comme l'ont souligné les commentaires, certains cas avec certains compilateurs, options de build et cibles pourraient rendre cela paradoxalement faux.
la source
x
est calculé dans un registre de la première ligne, en gardant plus de précision que le minimum pour afloat
. Ley = x
peut être en mémoire, ne gardant que lafloat
précision. Ensuite, le test d'égalité se ferait avec la mémoire par rapport au registre, avec des précisions différentes, et donc sans garantie.x+pow(b,2)==x+pow(a,3)
pourrait différer de l'auto one=x+pow(b,2); auto two=y+pow(a,3); one==two
un car on pourrait comparer en utilisant plus de précision que l'autre (si un / deux sont des valeurs de 64 bits en RAM, alors que les valeurs intermédistes sont de 80ish bits sur fpu). La mission peut donc parfois faire quelque chose.gcc -ffloat-store
pour une stricte conformité. Mais cette question concernex=y; x==y;
sans rien faire entre les deux. Siy
est déjà arrondi pour tenir dans un flotteur, la conversion en double ou en double long et retour ne changera pas la valeur. ...Oui,
y
prendra assurément la valeur dex
:Il n'y a pas de latitude pour attribuer d'autres valeurs.
(D'autres ont déjà souligné qu'une comparaison d'équivalence
==
sera néanmoins évaluéefalse
pour les valeurs de NaN.)Le problème habituel avec la virgule flottante
==
est qu'il est facile de ne pas avoir la valeur que vous pensez avoir. Ici, nous savons que les deux valeurs, quelles qu'elles soient, sont identiques.la source
[expr]
. Si je dois ignorer les liens et me concentrer sur les citations, je me retrouve avec la confusion que, par exemple, C.5.3 ne semble pas aborder l'utilisation du terme "valeur" ou du terme "résultat" (bien qu'il le fasse utiliser "result" une fois dans son contexte anglais normal). Peut-être pourriez-vous décrire plus clairement où vous pensez que la norme fait une distinction et fournir une citation claire unique à ce qui se passe. Merci!Oui, dans tous les cas (sans tenir compte des problèmes NaN et x87), cela sera vrai.
Si vous faites un
memcmp
sur eux, vous pourrez tester l'égalité tout en étant capable de comparer les NaN et les sNaN. Cela nécessitera également que le compilateur prenne l'adresse de la variable qui contraindra la valeur en 32 bitsfloat
au lieu de 80 bits. Cela éliminera les problèmes de x87. La deuxième affirmation ici est destinée à ne pas montrer que==
les NaN ne seront pas comparés comme vrais:Notez que si les NaN ont une représentation interne différente (c'est-à-dire une mantisse différente), la valeur
memcmp
ne sera pas vraie.la source
Dans les cas habituels, il serait évalué comme vrai. (ou l'instruction assert ne fera rien)
Modifier :
Par «cas habituels», j'entends exclure les scénarios susmentionnés (tels que les valeurs NaN et les unités à virgule flottante 80x87), comme indiqué par d'autres utilisateurs.
Étant donné l'obsolescence des puces 8087 dans le contexte actuel, le problème est plutôt isolé et pour que la question soit applicable dans l'état actuel de l'architecture à virgule flottante utilisée, elle est vraie dans tous les cas, sauf pour les NaN.
(référence environ 8087 - https://home.deec.uc.pt/~jlobo/tc/artofasm/ch14/ch143.htm )
Félicitations à @chtz pour avoir reproduit un bon exemple et @kmdreko pour avoir mentionné les NaN - ne les connaissaient pas avant!
la source
x
d'être dans un registre à virgule flottante pendant ley
chargement de la mémoire. La mémoire peut avoir moins de précision qu'un registre, ce qui entraîne l'échec de la comparaison.float
valeur sans précision supplémentaire.int a=1; int b=a; assert( a==b );
une assertion, je pense qu'il est logique de répondre à cette question en relation avec un compilateur fonctionnant correctement (tout en notant peut-être que certaines versions de certains compilateurs ont / ont -être connu-pour se tromper). En termes pratiques, si pour une raison quelconque, un compilateur ne supprime pas la précision supplémentaire du résultat d'une affectation stockée dans un registre, il doit le faire avant d' utiliser cette valeur.Oui, il retournera toujours True , sauf si c'est NaN . Si la valeur de la variable est NaN, elle renvoie toujours False !
la source