Si supponga che t
, a
, b
sono tutte doppie (IEEE Std 754) variabili, ed entrambi i valori di a
, b
NON sono NaN
(ma può essere Inf
). Dopo il t = a - b
, ho necessariamente a == b + t
?IEEE Std 754 virgola mobile: let t: = a - b, lo standard garantisce che a == b + t?
risposta
Assolutamente no. Un caso ovvio è a=DBL_MAX
, b=-DBL_MAX
. Quindi t=INFINITY
, quindi b+t
è anche INFINITY
.
Ciò che può essere più sorprendente è che ci sono casi in cui ciò accade senza overflow. Fondamentalmente, sono tutti della forma in cui a-b
non è esatto. Ad esempio, se è a
DBL_EPSILON/4
e b
è -1
, a-b
è 1 (supponendo modalità di arrotondamento di default), e a-b+b
è quindi 0.
Lo ricordo perché questo secondo esempio è che questo è il canonica modo di forzare arrotondamento ad una precisione particolare nell'aritmetica IEEE. Ad esempio, se si dispone di un numero compreso nell'intervallo [0,1) e si desidera forzare l'arrotondamento a 4 bit di precisione, è necessario aggiungere e quindi sottrarre 0x1p49
.
Il secondo esempio è ottimo in quanto non incorre in Inf né in NaN. Molte grazie. – updogliu
Si potrebbe voler chiarire la costante '0x1p49', l'ultima volta che ho guardato le cifre esadecimali da 0 a F;) – MSalters
@MSalters:" 0x1p49 "è il punto decimale esadecimale, come definito nello standard C. Il formato è "0x"
Nel processo di esecuzione della prima operazione, i bit potrebbero essere stati persi dal limite inferiore del risultato. Quindi una domanda è, la seconda operazione riprodurrà esattamente quelle perdite? Non l'ho completamente pensato.
Ma, ovviamente, la prima operazione potrebbe essere traboccata a +/- infinito, rendendo il secondo confronta diseguale.
(E, naturalmente, nel caso generale utilizzando ==
per i valori in virgola mobile è quasi sempre un errore.)
Solo con un argomento di conteggio, la seconda operazione non può riportare ciò che è stato perso. Se fosse possibile, memorizzeresti più bit di informazione in 't' rispetto al numero di bit in' t' ... –
@R - Sì. Intuitivamente si sa che non funzionerà, a causa di ciò che dici, ma trovare esempi è una "prova" migliore di quella che si appella a una regola esoterica, non importa quanto valida. –
Non sono garantiti nulla quando si usano galleggianti. Se l'esponente è diverso per entrambi i numeri, il risultato di un'operazione aritmetica potrebbe non essere completamente rappresentabile in un float.
Considerate questo codice:
float a = 0.003f;
float b = 10000000.0f;
float t = a - b;
float x = b + t;
esecuzione su Visual Studio 2010, si ottiene t==-10000000.0f
, e quindi x==0
.
Non si dovrebbe mai usare l'uguaglianza quando si confrontano i galleggianti. Confronta invece il valore assoluto della differenza tra entrambi i valori e un valore epsilon abbastanza piccolo per le tue esigenze di precisione.
Diventa ancora più strano in quanto diverse implementazioni in virgola mobile possono restituire risultati diversi per la stessa operazione.
Non mi è mai piaciuto il consiglio "confrontare il valore assoluto della differenza". È possibile ottenere limiti agli errori ([Ciò che ogni scienziato informatico dovrebbe sapere circa l'aritmetica virgola mobile] (http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html) è un buon inizio) e si dovrebbe pensare a cosa si sta cercando di realizzare con il confronto prima di passare ciecamente ad un limite arbitrario. –
Ci sono molte cose che sono garantite quando si usano i float IEEE-754. Questo capita di non essere uno di loro. –
Ci sono molte garanzie nell'uso dei float IEEE, e ci sono momenti in cui il confronto per l'uguaglianza non è solo ragionevole, ma essenziale. La matematica a virgola mobile è decisamente difficile, ma non è casuale o dannosa. Ecco un esempio del mio blog su quando testare l'uguaglianza in virgola mobile è fondamentale: https://randomascii.wordpress.com/2014/01/27/theres-only-four-billion-floatsso-test-them-all/ –
Credo che il risultato di un underflow non sarebbe definito, così come quello di un overflow nella seconda espressione, quindi no. Se qualcuno potesse confermarlo, sarebbe carino. – chris
Ah, immagino che questo tipo di conferma di overflow sia indefinito anche per virgola mobile: 'Come con qualsiasi altro flusso aritmetico, se il risultato non si adatta nello spazio fornito, il comportamento è indefinito. – chris
In un'implementazione C conforme a IEEE 754, non esiste UB per qualsiasi aritmetica in virgola mobile. Tutti i risultati sono rigorosamente definiti. –