Pregunta

Tengo algo de código simple que compara dos valores de coma flotante para ilustrar un problema que veo con la optimización de GCC y estoy esperando que alguien me puede ayudar a entender por qué la salida que produce es diferente en algunas circunstancias repetibles.

En primer lugar, yo sé que es malo para comparar valores de coma flotante con ==, ya que puede estar fuera por alguna cantidad muy pequeña en la mantisa, sin embargo, que no es el caso en mi ejemplo. El problema que tengo es la salida cambia en base a 2 factores. 1) la bandera que pase, y la optimización 2) si yo elimine la línea std :: cout.

¿Por qué el CCG producir código ejecuta de manera diferente bajo O2? ¿Por qué el código compilado bajo O2 trabajo si me elimine el comentario de impresión?

Este es el código que estoy probando:

#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;
}

Cuando se compila en Linux / cygwin con -O0, -O1, y -O3 (g ++ -O test.cpp), consigo el siguiente resultado:


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


Esa salida es correcta, se puede ver los bits para los flotadores (new_val y old_val) son idénticos. Cuando compilo con la bandera -O2 (g ++ -O2 test.cpp) consigo el siguiente:


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


Me parece que es equivocado de salida. A pesar de que los dos valores son los mismo bit sabia, restando ellos y el cheque == indican que son diferentes. Si a continuación, elimine la línea std :: cout, y reconstruir con la bandera -O2 (g ++ -O2 test.cpp) consigo el siguiente:


$ ./a.exe
COUT al azar que hace que este trabajo
old_val! = new_val
0,40c3126f
diff_val = -6,096
COUT al azar que hace que este trabajo
old_val == new_val
40c3126f, 40c3126f
diff_val = 1.19209e-07


Esto es correcto en ese old_val == new_val, a pesar de que la resta todavía muestra una ligera diferencia.

Este código también funciona bajo O2 si los pies es de 2000, en lugar de 20000.

Puede alguien explicar por qué el código compilado se comporta de esta manera? Quiero saber qué valores flotantes idénticas 2 bits no pueden ser comparados con ==.

gcc versión 3.4.4

¿Fue útil?

Solución

El nivel de optimización y que rodea código puede afectar si los valores utilizados en el cálculo diff_val se han recuperado de la memoria, o de registros. El procesador puede ser el uso de 80 bits registros internos de coma flotante en un caso, y de 32 bits flotante señalar los valores de la memoria en el otro caso, dando resultados inesperados.

Sin embargo, otra razón para evitar el uso de == para las comparaciones en coma flotante!

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top