Dans mon code,
float f = -0.0; // Negative
et par rapport à zéro négatif
f == -0.0f
le résultat sera true
.
Mais
float f = 0.0; // Positive
et par rapport à zéro négatif
f == -0.0f
le résultat sera également true
au lieu de false
Pourquoi dans les deux cas, le résultat est vrai?
Voici n MCVE pour le tester (en direct sur coliru) :
#include <iostream>
int main()
{
float f = -0.0;
std::cout<<"==== > " << f <<std::endl<<std::endl;
if(f == -0.0f)
{
std::cout<<"true"<<std::endl;
}
else
{
std::cout<<"false"<<std::endl;
}
}
Sortie:
==== > -0 // Here print negative zero
true
L'arithmétique à virgule flottante en C++ est souvent IEEE-754 . Cette norme diffère de la définition mathématique de l'ensemble de nombres réels.
Cette norme définit deux représentations différentes pour la valeur zéro: zéro positif et zéro négatif . Il est également défini que ces deux représentations doivent comparer des égaux, donc par définition:
+0.0 == -0.0
Quant à savoir pourquoi il en est ainsi, dans son article Ce que tout informaticien devrait savoir sur l'arithmétique à virgule flottante , David Goldberg, 1991-03 (lié dans la page IEEE-754 sur le site Web de l'IEEE) écrit:
Dans l'arithmétique IEEE, il est naturel de définir log 0 = -∞ et log x pour être un NaN lorsque x <0. Supposons que x représente un petit nombre négatif qui est descendu à zéro. Grâce au zéro signé, x sera négatif, donc log pourra retourner un NaN. Cependant, s'il n'y avait pas de zéro signé, la fonction log ne pourrait pas distinguer un nombre négatif sous-jacent de 0, et devrait donc retourner -∞.
C'est parce que le zéro négatif signé doit comparer true
avec zéro: c'est-à-dire -0.0 == 0.0
, -0f == 0f
, et -0l == 0l
.
C'est une exigence de tout schéma à virgule flottante pris en charge par un compilateur C++.
(Notez que la plupart des plateformes utilisent de nos jours la virgule flottante IEEE754, et ce comportement est explicitement documenté dans cette spécification.)
C++ 11 a introduit des fonctions comme std::signbit()
qui peuvent détecter des zéros signés et std::copysign()
qui peuvent copier le bit de signe entre flottant valeurs en points, si l'implémentation prend en charge le zéro signé (par exemple en raison de l'utilisation de virgule flottante IEEE). Ce genre de chose à part, je ne connais aucune référence dans une norme C++ qui mentionne même des zéros signés, sans parler du résultat de leur comparaison
Les normes C++ ne stipulent pas non plus de représentation en virgule flottante - définie par l'implémentation.
Bien qu'elles ne soient pas définitives, ces observations suggèrent que la prise en charge des zéros signés, ou le résultat de leur comparaison, serait déterminé par la représentation en virgule flottante prise en charge par l'implémentation (aka compilateur).
IEEE-754 est la représentation en virgule flottante la plus courante (mais pas la seule) utilisée par les implémentations modernes (c'est-à-dire les compilateurs). La version actuelle (publiée en 2008) de la section 5.11 de la norme IEEE-758 "Norme IEEE pour l'arithmétique à virgule flottante", dit (gras en gras)
Quatre relations mutuellement exclusives sont possibles: inférieur à, égal, supérieur à et non ordonné. Le dernier cas se présente quand au moins un opérande est NaN. Chaque NaN doit comparer non ordonné avec tout, y compris lui-même. Les comparaisons doivent ignorer le signe de zéro (donc +0 = −0). Les opérandes infinis du même signe doivent comparer égal.