La norme IEEE 754-2008 pour l'arithmétique à virgule flottante et la norme ISO / CEI 10967 pour l'arithmétique indépendante du langage (LIA), partie 1 expliquent pourquoi il en est ainsi.
IEEE 754 § 6.3 Le bit de signe
Lorsqu'une entrée ou un résultat est NaN, cette norme n'interprète pas le signe d'un NaN. Notez, cependant, que les opérations sur les chaînes de bits - copie, négation, abs, copySign - spécifient le bit de signe d'un résultat NaN, parfois basé sur le bit de signe d'un opérande NaN. Le prédicat logique totalOrder est également affecté par le bit de signe d'un opérande NaN. Pour toutes les autres opérations, cette norme ne spécifie pas le bit de signe d'un résultat NaN, même lorsqu'il n'y a qu'une seule entrée NaN, ou lorsque le NaN est produit à partir d'une opération non valide.
Lorsque ni les entrées ni le résultat ne sont NaN, le signe d'un produit ou d'un quotient est le OU exclusif des signes des opérandes; le signe d'une somme, ou d'une différence x - y considérée comme une somme x + (−y), diffère d'au plus un des signes d'addition; et le signe du résultat des conversions, l'opération de quantification, les opérations roundTo-Integral et roundToIntegralExact (voir 5.3.1) est le signe du premier ou du seul opérande. Ces règles s'appliquent même lorsque les opérandes ou les résultats sont nuls ou infinis.
Lorsque la somme de deux opérandes avec des signes opposés (ou la différence de deux opérandes avec des signes similaires) est exactement zéro, le signe de cette somme (ou différence) doit être +0 dans tous les attributs de sens d'arrondi sauf roundTowardNegative; sous cet attribut, le signe d'une somme nulle exacte (ou d'une différence) sera de −0. Cependant, x + x = x - (−x) conserve le même signe que x même lorsque x est égal à zéro.
Le cas de l'addition
Sous le mode d'arrondi par défaut (Round-to-Nearest, Ties-to-Even) , nous voyons que x+0.0
produit x
, SAUF quand x
est -0.0
: Dans ce cas, nous avons une somme de deux opérandes avec des signes opposés dont la somme est zéro, et §6.3 paragraphe 3 règles que cet ajout produit +0.0
.
Comme il +0.0
n'est pas identique au niveau du bit à l'original -0.0
, et qu'il -0.0
s'agit d'une valeur légitime qui peut apparaître en entrée, le compilateur est obligé de mettre le code qui transformera les zéros négatifs potentiels en +0.0
.
Le résumé: Dans le mode d'arrondi par défaut, dans x+0.0
, six
- n'est pas
-0.0
, alors x
elle-même est une valeur de sortie acceptable.
- is
-0.0
, alors la valeur de sortie doit être +0.0
, qui n'est pas identique au niveau du bit à -0.0
.
Le cas de la multiplication
Dans le mode d'arrondi par défaut , aucun problème de ce type ne se produit avec x*1.0
. Si x
:
Le cas de la soustraction
Dans le mode d'arrondi par défaut , la soustraction x-0.0
est également un no-op, car elle équivaut à x + (-0.0)
. Si x
c'est
- is
NaN
, alors §6.3p1 et §6.2.3 s'appliquent à peu près de la même manière que pour l'addition et la multiplication.
- est
+/- infinity
, alors le résultat est +/- infinity
du même signe.
- est un nombre (sous) normal,
x-0.0 == x
toujours.
- c'est
-0.0
donc au §6.3p2 que nous avons « [...] le signe d'une somme, ou d'une différence x - y considérée comme une somme x + (−y), diffère d'au plus un des signes d'addition; ". Cela nous oblige à attribuer -0.0
à la suite de (-0.0) + (-0.0)
, car le -0.0
signe ne diffère d' aucun des compléments, tandis que le +0.0
signe diffère de deux des compléments, en violation de cette clause.
- est
+0.0
, alors cela se réduit au cas d'addition (+0.0) + (-0.0)
considéré ci-dessus dans Le cas de l'addition , qui par §6.3p3 est censé donner +0.0
.
Puisque dans tous les cas, la valeur d'entrée est légale comme sortie, il est permis de considérer x-0.0
un no-op et x == x-0.0
une tautologie.
Optimisations qui changent de valeur
La norme IEEE 754-2008 a la citation intéressante suivante:
IEEE 754 § 10.4 Signification littérale et optimisations de changement de valeur
[...]
Les transformations de changement de valeur suivantes, entre autres, préservent la signification littérale du code source:
- Application de la propriété d'identité 0 + x lorsque x n'est pas nul et n'est pas un NaN de signalisation et que le résultat a le même exposant que x.
- Application de la propriété d'identité 1 × x lorsque x n'est pas un NaN de signalisation et que le résultat a le même exposant que x.
- Modification de la charge utile ou du bit de signe d'un NaN silencieux.
- [...]
Puisque tous les NaN et tous les infinis partagent le même exposant, et que le résultat correctement arrondi de x+0.0
et x*1.0
pour fini x
a exactement la même grandeur que x
, leur exposant est le même.
sNaNs
Les NaN de signalisation sont des valeurs d'interruption à virgule flottante; Ce sont des valeurs NaN spéciales dont l'utilisation comme opérande à virgule flottante entraîne une exception d'opération non valide (SIGFPE). Si une boucle qui déclenche une exception était optimisée, le logiciel ne se comporterait plus de la même manière.
Cependant, comme le fait remarquer user2357112 dans les commentaires , le standard C11 laisse explicitement indéfini le comportement de signalisation NaNs ( sNaN
), de sorte que le compilateur est autorisé à supposer qu'ils ne se produisent pas, et donc que les exceptions qu'ils soulèvent ne se produisent pas non plus. Le standard C ++ 11 omet de décrire un comportement pour la signalisation des NaN, et le laisse donc également indéfini.
Modes d'arrondi
Dans les modes d'arrondi alternés, les optimisations autorisées peuvent changer. Par exemple, en mode Round-to-Negative-Infinity , l'optimisation x+0.0 -> x
devient admissible, mais x-0.0 -> x
devient interdite.
Pour éviter que GCC n'assume les modes et comportements d'arrondi par défaut, le drapeau expérimental -frounding-math
peut être passé à GCC.
Conclusion
Clang et GCC , même à -O3
, restent conformes à la norme IEEE-754. Cela signifie qu'il doit respecter les règles ci-dessus de la norme IEEE-754. x+0.0
n'est pas un peu identique à x
for all x
selon ces règles, mais x*1.0
peut être choisi comme tel : à savoir, lorsque nous
- Obéissez à la recommandation de transmettre inchangée la charge utile de
x
quand il s'agit d'un NaN.
- Laissez le bit de signe d'un résultat NaN inchangé par
* 1.0
.
- Obéissez à l'ordre de XOR le bit de signe pendant un quotient / produit, quand
x
n'est pas un NaN.
Pour activer l'optimisation IEEE-754-unsafe (x+0.0) -> x
, l'indicateur -ffast-math
doit être passé à Clang ou GCC.