Lo standard IEEE 754-2008 per l'aritmetica in virgola mobile e lo standard ISO / IEC 10967 per l'aritmetica indipendente dal linguaggio (LIA), parte 1, rispondono al perché.
IEEE 754 § 6.3 Il bit di segno
Quando un input o un risultato è NaN, questo standard non interpreta il segno di un NaN. Si noti, tuttavia, che le operazioni sulle stringhe di bit - copia, negazione, abs, copiaSegna - specificano il bit di segno di un risultato NaN, a volte basato sul bit di segno di un operando NaN. Il predicato logico totalOrder è anche influenzato dal bit di segno di un operando NaN. Per tutte le altre operazioni, questo standard non specifica il bit di segno di un risultato NaN, anche quando esiste un solo NaN di input o quando il NaN viene prodotto da un'operazione non valida.
Quando né gli input né i risultati sono NaN, il segno di un prodotto o quoziente è l'OR esclusivo dei segni degli operandi; il segno di una somma, o di una differenza x - y considerata come una somma x + (−y), differisce al massimo da uno dei segni degli addend; e il segno del risultato di conversioni, l'operazione di quantizzazione, le operazioni roundTo-Integral e roundToIntegralExact (vedi 5.3.1) è il segno del primo o unico operando. Queste regole si applicano anche quando operandi o risultati sono zero o infiniti.
Quando la somma di due operandi con segni opposti (o la differenza di due operandi con segni simili) è esattamente zero, il segno di tale somma (o differenza) deve essere +0 in tutti gli attributi di direzione di arrotondamento ad eccezione di roundTowardNegative; in base a tale attributo, il segno di una somma (o differenza) zero esatta deve essere −0. Tuttavia, x + x = x - (−x) mantiene lo stesso segno di x anche quando x è zero.
Il caso di aggiunta
Sotto la modalità di arrotondamento predefinita (Round-to-più vicino, Ties-to-Even) , vediamo che x+0.0
produce x
, TRANNE quando x
è -0.0
: In quel caso abbiamo una somma di due operandi con segni opposti la cui somma è zero e §6.3 paragrafo 3 regole prodotte da questa aggiunta +0.0
.
Dal momento che +0.0
non è identico in modo bit a bit rispetto all'originale -0.0
e che -0.0
è un valore legittimo che può verificarsi come input, il compilatore è obbligato a inserire il codice che trasformerà i potenziali zeri negativi in +0.0
.
Il riepilogo: nella modalità di arrotondamento predefinita, in x+0.0
, ifx
- non lo è
-0.0
, quindi esso x
stesso è un valore di output accettabile.
- è
-0.0
, quindi il valore di output deve essere +0.0
, che non è identico a bit bit a -0.0
.
Il caso della moltiplicazione
Nella modalità di arrotondamento predefinita , non si verifica questo problema x*1.0
. Se x
:
Il caso della sottrazione
Nella modalità di arrotondamento predefinita , la sottrazione x-0.0
è anche no-op, poiché è equivalente a x + (-0.0)
. Se x
è
- è
NaN
, quindi §6.3p1 e §6.2.3 si applicano più o meno allo stesso modo dell'aggiunta e della moltiplicazione.
- è
+/- infinity
, quindi il risultato è +/- infinity
dello stesso segno.
- è un numero (sub) normale,
x-0.0 == x
sempre.
- è
-0.0
, quindi con §6.3p2 abbiamo " [...] il segno di una somma, o di una differenza x - y considerata come una somma x + (−y), differisce al massimo da uno dei segni degli addend; ". Questo ci obbliga ad assegnare -0.0
come risultato di (-0.0) + (-0.0)
, perché -0.0
differisce nel segno da nessuno dei dipendenti, mentre +0.0
differisce nel segno da due dei dipendenti, in violazione di questa clausola.
- è
+0.0
, quindi questo si riduce al caso di aggiunta (+0.0) + (-0.0)
considerato sopra in The Case of Addition , che in base al §6.3p3 è stato deciso di dare +0.0
.
Poiché in tutti i casi il valore di input è legale come output, è possibile considerare x-0.0
una no-op e x == x-0.0
una tautologia.
Ottimizzazioni che cambiano valore
Lo standard IEEE 754-2008 ha la seguente citazione interessante:
IEEE 754 § 10.4 Significato letterale e ottimizzazioni che cambiano valore
[...]
Le seguenti trasformazioni che cambiano valore, tra le altre, preservano il significato letterale del codice sorgente:
- Applicando la proprietà dell'identità 0 + x quando x non è zero e non è un NaN di segnalazione e il risultato ha lo stesso esponente di x.
- Applicando la proprietà identità 1 × x quando x non è un NaN di segnalazione e il risultato ha lo stesso esponente di x.
- Modifica del payload o firma un bit di un NaN silenzioso.
- [...]
Poiché tutti i NaN e tutti gli infiniti condividono lo stesso esponente e il risultato correttamente arrotondato di x+0.0
e x*1.0
per finito x
ha esattamente la stessa grandezza di x
, il loro esponente è lo stesso.
SNAN segnalano
I NaN di segnalazione sono valori di trap a virgola mobile; Sono valori NaN speciali il cui uso come operando a virgola mobile comporta un'eccezione di operazione non valida (SIGFPE). Se un loop che innesca un'eccezione fosse ottimizzato, il software non si comporterebbe più nello stesso modo.
Tuttavia, come sottolinea user2357112 nei commenti , lo standard C11 lascia esplicitamente indefinito il comportamento della segnalazione di NaN ( sNaN
), quindi al compilatore è consentito assumere che non si verifichino, e quindi anche le eccezioni che sollevano. Lo standard C ++ 11 omette di descrivere un comportamento per la segnalazione di NaN e quindi non lo definisce.
Modalità di arrotondamento
In modalità di arrotondamento alternativo, le ottimizzazioni consentite possono cambiare. Ad esempio, nella modalità Infinito da rotondo a negativo , l'ottimizzazione x+0.0 -> x
diventa consentita, ma x-0.0 -> x
diventa proibita.
Per impedire a GCC di assumere modalità e comportamenti di arrotondamento predefiniti, il flag sperimentale -frounding-math
può essere passato a GCC.
Conclusione
Clang e GCC , anche a -O3
, rimangono conformi a IEEE-754. Ciò significa che deve attenersi alle regole precedenti dello standard IEEE-754. nonx+0.0
è un po 'identico a x
tutti per x
quelle regole, ma x*1.0
può essere scelto per essere così : vale a dire, quando noi
- Rispettare la raccomandazione di passare invariato il carico utile di
x
quando si tratta di un NaN.
- Lascia invariato il bit di segno di un risultato NaN
* 1.0
.
- Rispettare l'ordine di XOR il bit di segno durante un quoziente / prodotto, quando non
x
è un NaN.
Per abilitare l'ottimizzazione IEEE-754 non sicura (x+0.0) -> x
, il flag -ffast-math
deve essere passato a Clang o GCC.