2010-09-21 13 views
6

Ho un codice semplice che confronta due valori float per illustrare un problema che vedo con l'ottimizzazione di GCC e spero che qualcuno possa aiutarmi a capire perché l'output che produce è diverso in alcuni circostanze ripetibili.Gestione GCC Confronto float in modo diverso a diversi livelli di ottimizzazione

In primo luogo, so che è male confrontare i valori float con == perché si può essere spenti di una quantità molto piccola nella mantissa, tuttavia ciò non è il caso nel mio esempio. Il problema che ho è l'output cambia in base a 2 fattori. 1) il flag di ottimizzazione I pass in, e 2) se rimoziono la linea std :: cout.

Perché il codice GCC generato funziona in modo diverso sotto -O2? Perché il codice compilato sotto -O2 funziona se annullo la stampa?

Ecco il codice che sto testando:

#include <iostream> 

const float ft_to_m   = (float)0.3048; 
const float m_to_ft   = (float)3.28083989501; 


float FeetToKilometers(float & Feet) { 
    float Kilometers; 
    Kilometers = (ft_to_m * Feet)/1000.; 
    return Kilometers; 
} 

int main(void) 
{ 
    float feet = 20000.; 
    float old_val = 0; 
    float new_val = FeetToKilometers(feet); 
    float diff_val = 0; 

    int *old_int = reinterpret_cast<int*>(&old_val); 
    int *new_int = reinterpret_cast<int*>(&new_val); 

    for (int i=0; i<2; i++) 
    { 

    new_val = FeetToKilometers(feet); 
    diff_val = old_val-new_val; 

    //std::cout << "Random COUT that makes this work" << std::endl; 

     if(old_val==new_val) 
    { 
      std::cout << "old_val==new_val" << std::endl; 
     std::cout << std::hex << *old_int << "," << std::hex << *new_int << std::endl; 
      std::cout << "diff_val = " << diff_val <<std::endl; 
    } 
     else 
     { 
      std::cout << "old_val!=new_val" <<std::endl; 
     std::cout << std::hex << *old_int << "," << std::hex << *new_int << std::endl; 
      std::cout << "diff_val = " << diff_val <<std::endl; 
      old_val=FeetToKilometers(feet); 
     } 
    } 

    return 0; 
} 

Quando compilato su linux/Cygwin con -O0, -O1, e -O3 (g ++ -O test.cpp), ottengo il seguente output:


$ ./a.exe
old_val! = new_val
0,40c3126f
diff_val = -6,096
old_val == new_val
40c3126f, 40c3126f
diff_val = 0


Tale uscita è corretto, è possibile vedere i bit per i carri (new_val e old_val) sono identici. Quando compilo con il flag -O2 (g ++ -O2 test.cpp) ottengo il seguente:!


$ ./a.exe
old_val = new_val
0,40c3126f
diff_val = -6,096
old_val! = new_val
40c3126f, 40c3126f
diff_val = 1.19209e-07


Considererei questo output errato. Anche se i due valori sono lo identico allo bit, sottraendoli e il controllo == indicano che sono diversi. Se io rimuovere il commento dalla linea di std :: cout, e ricostruire con la bandiera -O2 (g ++ -O2 test.cpp) ottengo il seguente:


$ ./a.exe
a caso COUT che rende questo lavoro
old_val! = new_val
0,40c3126f
diff_val = -6.096
a caso COUT che rende questo lavoro
old_val == new_val
40c3126f, 40c3126f
diff_val = 1.19209e-07


Questo è corretta in quanto new_val == old_val, anche se il la sottrazione mostra ancora una leggera differenza.

Questo codice funziona anche sotto -O2 se i piedi è di 2000, invece di 20000.

Qualcuno può spiegare il motivo per cui il codice compilato si comporta in questo modo? Voglio sapere perché i valori float identici a 2 bit non possono essere confrontati con ==.

versione gcc 3.4.4

risposta

10

Il livello di ottimizzazione e codice circostante possono influenzare se i valori utilizzati nel calcolo diff_val vengono recuperati dalla memoria, o da registri. Il processore può utilizzare registri a virgola mobile interno a 80 bit in un caso e valori punti mobili a 32 bit dalla memoria nell'altro caso, dando risultati imprevisti.

Un altro motivo per evitare l'utilizzo di == per confronti a virgola mobile!

+2

+1. Questo è (quasi sicuramente) il problema. La compilazione con l'opzione '-ffloat-store' fornisce risultati coerenti indipendentemente dal livello di ottimizzazione. – eldarerathis

+0

Okay, quindi forse sono un noob ma come si fa a fare confronti a virgola mobile? <= and > =? Sarebbe sufficiente un link alle informazioni pertinenti. Grazie. –

+0

@Robert: 'fabs (a-b)