Передача типов double в ceil приводит к разным значениям для разных уровней оптимизации в GCC.

Ниже значения переменных result1 и result2 сообщают разные значения в зависимости от того, компилируете ли вы код с помощью -g или с -O в GCC 4.2.1 и GCC 3.2.0 (и я не пробовал более свежие версии GCC) :

double double_identity(double in_double)
{
    return in_double;
}

...

double result1 = ceil(log(32.0) / log(2.0));
std::cout << __FILE__ << ":" << __LINE__ << ":" << "result1==" << result1 << std::endl;

double result2 = ceil(double_identity(log(32.0) / log(2.0)));
std::cout << __FILE__ << ":" << __LINE__ << ":" << "result2==" << result2 << std::endl;

result1 и result2 == 5 только при компиляции с использованием -g, но если вместо этого я компилирую с -O, я получаю result1 == 6 и result2 == 5.

Это похоже на разницу в том, как оптимизация выполняется компилятором, или что-то связанное с представлением с плавающей запятой IEEE внутри, но мне любопытно, как именно возникает эта разница. Я надеюсь, что вообще не буду смотреть на ассемблер.

Вышеупомянутое было скомпилировано на C ++, но я предполагаю, что то же самое было бы, если бы оно было преобразовано в код ANSI-C с помощью printfs.

Вышеуказанное несоответствие наблюдается в 32-битном Linux, но не в 64-битном Linux.

Спасибо bg


person bgoodr    schedule 24.07.2010    source источник
comment
Урок, который вы должны извлечь из этого: не используйте журнал с плавающей запятой / pow / и т. Д. функции (или любые числа с плавающей запятой), если вы хотите выполнять точные целочисленные операции, если у вас нет докторской степени в области численного анализа.   -  person R.. GitHub STOP HELPING ICE    schedule 24.07.2010
comment
Согласовано. В свою защиту использованный мной фрагмент кода был урезанной копией кода другого разработчика, и, конечно же, он сломался, поэтому я здесь.   -  person bgoodr    schedule 24.07.2010


Ответы (1)


В x86 с включенной оптимизацией результаты подвыражений не обязательно сохраняются в 64-битной области памяти перед использованием как часть более крупного выражения.

Поскольку стандартные регистры x86 с плавающей запятой составляют 80 бит, это означает, что в таких случаях доступна дополнительная точность. ЕСЛИ вы затем разделите (или умножите) это особо точное значение на другое, эффекты повышенной точности могут усилиться до такой степени, что их можно будет заметить невооруженным глазом.

64-разрядные процессоры Intel используют регистры SSE для вычислений с плавающей запятой, и эти регистры не имеют дополнительной точности.

Вы можете поиграть с g ++ flags, чтобы исправить это, если вам действительно не все равно.

person Drew Hoskins    schedule 24.07.2010
comment
Я перевожу то место, где они могут быть восприняты невооруженным глазом, где значения из этих вычислений могут храниться в регистре с плавающей запятой шириной 80 бит, а затем передаваться в функцию ceil, которая послушно определяет дополнительную точность и возвращает следующее большее целое значение. - person bgoodr; 24.07.2010
comment
Я отмечаю это как ответ, потому что ваша ссылка на stackoverflow.com/questions/3234042/ привел меня к stackoverflow.com/questions/3234042/, и я попробовал это в приведенном выше коде, и он дал желаемые результаты. Теперь, конечно, у меня другая проблема в том, что я подозреваю, что в исходном исходном коде могут быть тонны такого же кода, который теперь будет вести себя иначе, если я выберу опцию -ffloat-store. - person bgoodr; 24.07.2010