Сразу предупреждаю, что это сложная задача.
Есть тест. Тест был результатом анализа большой проблемы с ошибкой, с которой мы столкнулись на работе. строительство __ attribute__((noinline))
запрещает компилятору делать функцию подстановки (для оптимизации чего-то там не взорвалось). Это самый простой способ оптимизации, гарантированно не убивающий интересную ситуацию.
#include <stdio.h>
double d = 5436277361664796672.000000;
long long ll = 5436277361664796253LL;
int __attribute__((noinline))
func1 (void)
{
double d1 = (double)ll;
if (d > d1)
return 1;
else
return 0;
}
int __attribute__((noinline))
func2 (void)
{
if (d > (double)ll)
return 1;
else
return 0;
}
int
main (void)
{
printf ("%d %d\n", func1(), func2());
return 0;
}
Я провел этот тест на intel
а также sparc
, Gcc
используется в режиме с оптимизацией и без оптимизации. Получены следующие результаты:
sparc: "gcc" printed "0 0"sparc: "gcc -O2" printed "0 0"intel: "gcc" printed "0 1"intel: "gcc -O2" printed "1 1"
В чем причина различия? В любом случае в ситуации анализа было бы полезно иметь возможность повторить все это самостоятельно, но, конечно, почти никто не имеет возможности запустить этот код на sparc
, Вместо sparc
можно попробовать запустить под Windows используя microsoft or borland C compiler
, Я не знаю, что им дадут результаты, но в любом случае что-то не соответствует ни с чем (потому что мы видим три разных результата)
Редактировать 1
_атрибут_ ((noinline)) — расширение компилятора gcc (забыл написать об этом). Поэтому VisualStudio не может скомпилировать его.
Я отмечаю, что объявление двойной константы имеет 19 значащих цифр, что является более точным, чем то, что может быть представлено двойным IEEE (который допускает 15-17 значащих цифр). Таким образом, d не может содержать 5436277361664796672.000000 точно.
Две строки определения констант различаются на 16-й цифре, поэтому вы находитесь в той области, где неточности в двойном выражении имеют ту же величину, что и разница между этими двумя числами. Следовательно, нельзя сравнивать сравнение.
Я не знаю, определяет ли стандарт C ++, что происходит, когда сверхточная строка преобразуется в double, но я не удивлюсь, если точный результат будет либо неопределенным, либо зависящим от реализации.
Кажется, решена проблема. В целом все написано правильно. Но на самом деле работает правильно sparc
версия. Потому что стандарт для преобразования int64-> float64
должна быть потеря точности. И в коде при конвертации (для intel) int64-> float80
потеря происходит. Т.е. код на основе Intel работает с большей точностью, но это противоречит стандарту.
Возможно, это какое-то соглашение для платформы Intel, которое по умолчанию разрешено работать таким образом. Конечно, есть некоторые варианты, в которых код выполняется в строгом соответствии со стандартом (но становится медленнее)