У мене є простий код, який порівнює два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-розрядні з плаваючою комою точкові значення з пам'яті в іншому випадку, даючи несподівані результати.
Ще одна причина уникати використання ==
для порівнянь із плаваючою комою!