168 votes

Si je copie un flottant dans une autre variable, seront-ils égaux ?

Je sais qu'en utilisant == pour vérifier l'égalité des variables à virgule flottante n'est pas une bonne méthode. Mais je veux juste le savoir avec les déclarations suivantes :

float x = ...

float y = x;

assert(y == x)

Desde y est copié à partir de x l'affirmation sera-t-elle vraie ?

78 votes

Permettez-moi d'offrir une prime de 50 euros à celui qui prouvera réellement l'inégalité par une démonstration avec du code réel. Je veux voir le truc 80 vs 64 bits en action. Plus 50 autres pour une explication du code assembleur généré qui montre qu'une variable est dans un registre et que l'autre ne l'est pas (ou quelle que soit la raison de l'inégalité, j'aimerais que ce soit expliqué à un bas niveau).

1 votes

@ThomasWeller le bug de GCC à ce sujet : gcc.gnu.org/bugzilla/show_bug.cgi?id=323 Cependant, je viens d'essayer de le reproduire sur un système x86-64 et ça ne marche pas, même avec -ffast-math. Je soupçonne que vous avez besoin d'un vieux GCC sur un système 32 bits.

5 votes

@pjc50 : En fait, vous avez besoin d'un système 80 bits pour reproduire le bug 323 ; c'est la FPU 80x87 qui a causé le problème. x86-64 utilise la FPU SSE. Les bits supplémentaires causent le problème, car ils sont arrondis lors du déversement d'une valeur dans un flottant 32 bits.

129voto

chtz Points 6357

En plus de la assert(NaN==NaN); Dans le cas signalé par kmdreko, il peut y avoir des situations avec x87-math, lorsque des flottants 80bit sont temporairement stockés en mémoire et comparés plus tard à 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 :

#include <cassert>

int main(int argc, char**){
    float x = 1.f/(argc+2);
    volatile float y = x;
    assert(x==y);
}

Démonstration de Godbolt : https://godbolt.org/z/X-Xt4R

El volatile peut probablement être omis, si vous parvenez à créer une pression de registre suffisante pour avoir y stockée et rechargée depuis la mémoire (mais embrouille suffisamment le compilateur pour ne pas omettre complètement la comparaison).

Voir la référence de la FAQ GCC :

119voto

kmdreko Points 3321

Ce ne sera pas vrai si x es NaN puisque les comparaisons sur NaN sont toujours faux (oui, même NaN == NaN ). Pour tous les autres cas (valeurs normales, valeurs subnormales, infinis, zéros) cette assertion sera vraie.

Les conseils pour éviter == pour les flottants s'applique à calculs en raison de l'incapacité des nombres à virgule flottante à exprimer exactement de nombreux résultats 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 donne 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. À partir de <cfloat> hérité de C [5.2.4.2.2.8] ( c'est moi qui souligne ):

À l'exception de l'assignation et du cast (qui suppriment toute portée et précision supplémentaires). les valeurs des opérations avec des opérandes flottants et des valeurs soumises aux conversions arithmétiques habituelles et des constantes flottantes sont évaluées dans un format dont la plage et la précision peuvent être supérieures à celles requises par le type.

Cependant, comme les commentaires l'ont souligné, certains compilateurs, options de compilation et cibles peuvent être utilisés. pourrait rendent cette affirmation paradoxalement fausse.

36voto

Oui, y prendra assurément la valeur de x :

[expr.ass]/2 : Dans l'affectation simple (=), l'objet auquel se réfère l'opérande de gauche est modifié ([defns.access]) en remplaçant sa valeur par le résultat de l'opérande de droite.

Il n'y a aucune marge de manœuvre pour l'attribution d'autres valeurs.

(D'autres ont déjà fait remarquer qu'une comparaison d'équivalence == sera néanmoins évalué à false pour les valeurs NaN).

Le problème habituel avec les points flottants == est qu'il est facile de pas ont tout à fait la valeur que vous pensez avoir. Ici, nous savons que les deux valeurs, quelles qu'elles soient, sont les mêmes.

4voto

S.S. Anne Points 13829

Oui, dans tous les cas (sans tenir compte des NaN et des problèmes de x87), ce sera vrai.

Si vous faites un memcmp sur eux, vous pourrez tester l'égalité tout en étant capable de comparer les NaNs et les sNaNs. Cela nécessitera également que le compilateur prenne l'adresse de la variable, ce qui convertira la valeur en un code 32 bits. float au lieu d'un 80 bits. Cela éliminera les problèmes liés au x87. La deuxième affirmation ici a pour but d'échouer à montrer que == ne comparera pas les NaNs comme vrais :

#include <cmath>
#include <cassert>
#include <cstring>

int main(void)
{
    float x = std::nan("");
    float y = x;
    assert(!std::memcmp(&y, &x, sizeof(float)));
    assert(y == x);
    return 0;
}

Notez que si les NaNs ont une représentation interne différente (c'est-à-dire une mantisse différente), la fonction memcmp ne seront pas comparées à la réalité.

1voto

Anirban166 Points 3162

Dans les cas habituels, l'évaluation sera vraie (ou l'instruction assert ne fera rien).

Modifier :

Par "cas habituels", j'entends que j'exclue 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 pour tous les cas sauf pour les NaN.

(référence à propos de 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 - je ne les connaissais pas auparavant !

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X