Domanda

Ho avuto un piccolo momento WTF questa mattina. Ths WTF si può riassumere con questo:

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)

Il motivo sembra essere che il x + y espressione è promosso a raddoppiare e confrontata con la versione troncata in z. (Se cambio z al double l'asserzione non viene attivato).

Vedo che per motivi di precisione avrebbe senso per eseguire tutte aritmetica in virgola mobile a doppia precisione prima di convertire il risultato a singola precisione. Ho trovato il seguente paragrafo nella norma (che immagino io sorta di già sapevo, ma non in questo contesto):

4.6.1. "An rvalue di tipo float può essere convertito in un rvalue di tipo double. Il valore è invariato"

La mia domanda è, è x + y garantito per essere promosso a raddoppiare o è a discrezione del compilatore?

UPDATE: Dal momento che molte persone hanno sostenuto che non si dovrebbe usare per == in virgola mobile, volevo solo dire che nel caso specifico con cui sto lavorando, un confronto esatto è giustificata.

virgola mobile confronto è 'complicato, ecco un interessante sul tema che credo non è stata menzionata.

È stato utile?

Soluzione

Non si può in genere presumere che == funzionerà come previsto per il galleggiamento tipi di punti. Confrontare i valori arrotondati o utilizzare costrutti come abs(a-b) < tolerance invece.

La promozione è interamente a discrezione del compilatore (e dipenderà hardware di destinazione, livello di ottimizzazione, ecc).

Che cosa sta succedendo in questo caso particolare è quasi certamente che i valori sono memorizzati in FPU registra ad una precisione superiore a quella in memoria - in generale, moderno hardware FPU funziona con doppio o superiore precisione internamente qualunque sia la precisione il programmatore ha chiesto, con la codice generatrice compilatore per effettuare le conversioni appropriate quando i valori vengono memorizzati nella memoria; in una build unoptimised, il risultato di x+y è ancora in un registro a punto il confronto viene fatto ma z sarà stato memorizzato ad memoria e recuperato indietro, e quindi troncato a galleggiare precisione.

Altri suggerimenti

Il progetto di lavoro per la prossima serie C ++ 0x sezione 5, punto 11, dice

  

I valori degli operandi galleggianti e dei risultati delle espressioni galleggianti possono essere rappresentati in maggior precisione e la gamma di quello richiesto dal tipo; i tipi non sono cambiati così

Quindi, a discrezione del compilatore.

Utilizzando gcc 4.3.2, l'affermazione è non innescata, e in effetti, il rvalue tornato da x + y è un float, piuttosto che un double.

Quindi spetta al compilatore. Questo è il motivo per cui non è mai saggio fare affidamento sulla parità esatta tra due valori in virgola mobile.

Il ++ FAQ lite C ha qualche ulteriore discussione sul tema:

E 'il problema dal numero di float di conversione binaria non dà la precisione accurata.

E all'interno sizeof(float) byte cant accogliere valore preciso del numero di galleggiante e operazione aritmetica può portare ad approssimazione e quindi uguaglianza fallisce.

Vedi sotto per es.

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

Vorrei pensare che sarebbe a discrezione del compilatore, ma si potrebbe sempre forzo con un cast se era il vostro pensiero?

Ancora un altro motivo per non confrontare direttamente i galleggianti.

if (fabs(result - expectedResult) < 0.00001)
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top