/ / GCC обробка порівняння Float по-різному на різних рівнях оптимізації - linux, gcc, плаваюча точка, дорівнює, mantissa

GCC поводження з поплавковим порівнянням по-різному на різних рівнях оптимізації - linux, gcc, плаваюча точка, рівна, mantissa

У мене є простий код, який порівнює дваfloat values, щоб проілюструвати проблему, яку я бачу при оптимізації GCC, і сподіваюся, хтось може допомогти мені зрозуміти, чому вихідний результат, який він виробляє, відрізняється за деяких повторюваних обставин.

По-перше, я знаю, що погано порівнювати плаваючі значенняз ==, тому що ви можете вимкнути якусь дуже малу суму в мантисі, проте це не так у моєму прикладі. У мене проблема полягає у зміні результатів на основі 2 факторів. 1) прапор оптимізації, який я передаю, і 2) якщо я прокоментую рядок std :: cout.

Чому код GCC, який виробляється, працює по-різному під -O2? Чому код, скомпільований під -O2, працює, якщо я коментую друк?

Ось код, який я тестую:

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

При компіляції на Linux / cygwin з -O0, -O1 та -O3 (g ++ -O test.cpp), я отримую такий результат:


$ ./a.exe
старий_вал! = новий_вал
0,40c3126f
різниця_вал = -6,096
старий_вал == новий_вал
40c3126f, 40c3126f
різниця_вал = 0


Цей вивід правильний, ви можете бачити, що біти для поплавків (new_val та old_val) однакові. Коли я компілюю з прапором -O2 (g ++ -O2 test.cpp), я отримую таке:


$ ./a.exe
старий_вал! = новий_вал
0,40c3126f
різниця_вал = -6,096
старий_вал! = новий_вал
40c3126f, 40c3126f
diff_val = 1.19209e-07


Я вважаю цей результат неправильним. Хоча ці два значення є той же трохи мудро, віднімаючи їх і перевірка == вказує, що вони різні. Якщо я потім розкоментую рядок std :: cout і відновлю прапорець -O2 (g ++ -O2 test.cpp), я отримую таке:


$ ./a.exe
Випадковий COUT, що робить цю роботу
старий_вал! = новий_вал
0,40c3126f
різниця_вал = -6,096
Випадковий COUT, що робить цю роботу
старий_вал == новий_вал
40c3126f, 40c3126f
diff_val = 1.19209e-07


Це правильно в тому, що old_val == new_val, навіть якщо віднімання все ще показує невелику різницю.

Цей код також працює під -O2, якщо фут становить 2000, а не 20000.

Хтось може пояснити, чому скомпільований код поводиться так? Я хочу знати, чому 2-бітові однакові значення з плаваючою не можна порівнювати з ==

версія gcc 3.4.4

Відповіді:

10 за відповідь № 1

Рівень оптимізації та оточуючий код можуть впливати на те, чи використовуються значення в diff_val обчислення отримуються з пам'яті, абоз реєстрів. Процесор може використовувати в одному випадку 80-розрядні внутрішні регістри з плаваючою комою та 32-розрядні з плаваючою комою точкові значення з пам'яті в іншому випадку, даючи несподівані результати.

Ще одна причина уникати використання == для порівнянь із плаваючою комою!