Question

J'ai eu un petit moment de WTF ce matin. WTF Thessaloniciens peut se résumer avec ceci:

float x = 0.2f;
float y = 0.1f;
float z = x + y;
assert(z == x + y); //This assert is triggered! (Atleast with visual studio 2008)

La raison semble être que l'expression x + y est promu deux fois et comparée à la version tronquée en z. (Si je change z à double assert ne se déclenche pas).

Je peux voir que pour des raisons de précision, il serait logique d'effectuer toutes les arithmétiques en virgule flottante en double précision avant de convertir le résultat en simple précision. J'ai trouvé le paragraphe suivant dans la norme (que je suppose que je savais déjà en quelque sorte, mais pas dans ce contexte):

4.6.1. « Un rvalue de type float peut être converti en un rvalue de type double. La valeur est inchangée »

Ma question est, est x + y garanti d'être promu à doubler ou est à la discrétion du compilateur?

Mise à jour: Comme beaucoup de gens a affirmé que l'on devrait pas utiliser == pour virgule flottante, je voulais juste dire que dans le cas particulier, je travaille avec une comparaison exacte est justifiée.

virgule flottante comparision est difficile, voici un sur le sujet que je pense n'a pas été mentionné.

Était-ce utile?

La solution

Vous ne pouvez pas supposer généralement que == fonctionnera comme prévu pour les types de virgule flottante. Comparer les valeurs arrondies ou utiliser des constructions comme abs(a-b) < tolerance à la place.

La promotion est entièrement à la discrétion du compilateur (et dépendra du matériel cible, le niveau d'optimisation, etc.).

Qu'est-ce qui se passe dans ce cas particulier est presque certainement que les valeurs sont stockées dans FPU enregistre à une plus grande précision que dans la mémoire - en général, FPU matériel moderne fonctionne avec une précision double ou supérieure interne quelle que soit la précision du programmeur a demandé, avec le un code de génération de compilateur pour effectuer les conversions appropriées lorsque des valeurs sont stockées dans la mémoire; dans une construction non optimisé, le résultat de x+y est toujours dans un registre au moment où la comparaison est faite, mais z aura été stocké sur la mémoire et les cheveux en arrière, et tronquée ainsi flotter précision.

Autres conseils

Le projet de travail pour la prochaine norme C ++ 0x l'article 5 point 11 dit

  

Les valeurs des opérandes variables et les résultats des expressions flottantes peuvent être représentées dans une plus grande précision et la gamme que celle requise par le type; les types ne sont pas modifiés par là

Ainsi, à la discrétion du compilateur.

En utilisant gcc 4.3.2, l'affirmation est pas SERVICE, et en effet, le rvalue retourné de x + y est un float, plutôt que d'un double.

Il est au compilateur. C'est pourquoi il est sage de ne jamais compter sur l'égalité exacte entre deux valeurs à virgule flottante.

Le C ++ FAQ Lite a quelques discussions sur le sujet:

Il est le problème puisque nombre flottant à la conversion binaire ne donne pas de précision précise.

Et dans les sizeof(float) octets il ne peut pas accueillir valeur précise du nombre de flotteur et opération arithmétique peut conduire à une approximation et donc l'égalité échoue.

Voir ci-dessous par exemple.

float x = 0.25f; //both fits within 4 bytes with precision
float y = 0.50f;
float z = x + y;
assert(z == x + y); // it would work fine and no assert

Je pense que ce serait à vous pouvez toujours forcer la discrétion du compilateur, mais avec un casting si tel était votre pensée?

Encore une autre raison de ne jamais comparer directement les flotteurs.

if (fabs(result - expectedResult) < 0.00001)
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top