/ / GCC Handling Float comparação de maneira diferente em diferentes níveis de otimização - linux, gcc, ponto flutuante, igual, mantissa

GCC Manuseio da comparação Flutuante de maneira diferente em diferentes Níveis de Otimização - linux, gcc, ponto flutuante, igual, mantissa

Eu tenho um código simples que está comparando doisvalores flutuantes para ilustrar um problema que vejo com a otimização do GCC e espero que alguém possa me ajudar a descobrir por que a saída que ele produz é diferente em algumas circunstâncias repetíveis.

Primeiro, eu sei que é ruim comparar valores flutuantescom == porque você pode estar errado por uma quantia muito pequena na mantissa, porém esse não é o caso no meu exemplo. O problema que tenho são as mudanças de saída com base em 2 fatores. 1) o sinalizador de otimização que eu passo e 2) se eu descomentar a linha std :: cout.

Por que o código que o GCC produz é executado de maneira diferente em -O2? Por que o código compilado em -O2 funciona se eu descomentar a impressão?

Aqui está o código que estou 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 compilado em linux / cygwin com -O0, -O1 e -O3 (g ++ -O test.cpp), obtenho a seguinte saída:


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


Essa saída está correta, você pode ver que os bits para os flutuadores (new_val e old_val) são idênticos. Quando compilo com o sinalizador -O2 (g ++ -O2 test.cpp), obtenho o seguinte:


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


Eu consideraria esta saída errada. Mesmo que os dois valores sejam os mesmo bit a bit, subtraindo-os e a marca de verificação == indica que eles são diferentes. Se eu remover o comentário da linha std :: cout e reconstruir com a sinalização -O2 (g ++ -O2 test.cpp), obtenho o seguinte:


$ ./a.exe
COUT aleatório que faz este trabalho
old_val! = new_val
0,40c3126f
diff_val = -6.096
COUT aleatório que faz este trabalho
old_val == new_val
40c3126f, 40c3126f
diff_val = 1.19209e-07


Isso está correto naquele old_val == new_val, embora a subtração ainda mostre uma ligeira diferença.

Este código também funciona sob -O2 se pés forem 2.000, em vez de 20.000.

Alguém pode explicar por que o código compilado está se comportando assim? Eu quero saber por que valores float idênticos de 2 bits não podem ser comparados com ==.

gcc versão 3.4.4

Respostas:

10 para resposta № 1

O nível de otimização e o código circundante podem afetar se os valores usados ​​no diff_val cálculos estão sendo buscados na memória, oude registros. O processador pode estar usando registradores de ponto flutuante interno de 80 bits em um caso, e flutuante de 32 bits valores de ponto da memória no outro caso, dando resultados inesperados.

Mais uma razão para evitar o uso == para comparações de ponto flutuante!