У меня есть следующий бит кода, однако при компиляции его с GCC 4.4 с различными флагами оптимизации, я получаю некоторые неожиданные результаты когда ее выполнение.
#include
int main()
{
const unsigned int cnt = 10;
double lst[cnt] = { 0.0 };
const double v[4] = { 131.313, 737.373, 979.797, 731.137 };
for(unsigned int i = 0; i < cnt; ++i) {
lst[i] = v[i % 4] * i;
}
for(unsigned int i = 0; i < cnt; ++i) {
double d = v[i % 4] * i;
if(lst[i] != d) {
std::cout << "error @ : " << i << std::endl;
return 1;
}
}
return 0;
}
при компиляции с: "g ++ - педантичный - стенной-Werror-o1-o тест test.cpp" я получаю следующий вывод: "ошибка: 3 дюйма
при компиляции с: "g ++ - педантичный - стенной-Werror-o2-o тест test.cpp" я получаю следующий вывод: "ошибка: 3 дюйма
при компиляции с: "g ++ - педантичный - стенной-Werror-o3-o тест test.cpp" я не получаю ошибок
при компиляции с: "g ++ - педантичный - Стена-Werror-o тестирует test.cpp", я не получаю ошибок
Я не полагаю, что это проблема, связанная с округлением или различием в эпсилоне в сравнении. Я попробовал это Intel v10 и MSVC 9.0, и они все, кажется, работают как ожидалось. Я полагаю, что это должно быть не чем иным, как поразрядное выдерживают сравнение.
Если я заменяю оператор "if" следующим: if (static_cast
, и добавьте "-Wno-long-long", я не получаю ошибок ни в одном из режимов оптимизации, когда выполнено.
Если я добавляю std::cout << d << std::endl;
перед "возвратом 1", я не получаю ошибок ни в одном из режимов оптимизации, когда выполнено.
Действительно ли это - ошибка в моем коде или является там чем-то не так с GCC и способом, которым это обрабатывает двойной тип?
Примечание: Я только что попробовал это gcc версиями 4.3 и 3.3, ошибка не показана.
Разрешение: Mike Dinsdale отметил следующий отчет об ошибках: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=323 Это кажется командой GCC, не абсолютно уверен в природе проблемы.
Как предложено в отчете об ошибках возможное разрешение состоит в том, чтобы использовать опцию ffloat-хранилища. Я попробовал это, и это работает, однако результаты точки зрения производительности не являются настолько большими, хотя ymmv.
Тот факт, что результат зависит от настроек оптимизации, наводит на мысль, что это может быть ошибка расширенной точности x87 (как говорит Майкл Берр).
Вот код, который я использую (с gcc на процессорах x86) для отключения повышенной точности:
static const unsigned int PRECISION_BIT_MASK = 0x300;
///< bitmask to mask out all non-precision bits in the fpu control word \cite{INTEL}.
static const unsigned int EXTENDED_PRECISION_BITS = 0x300;
///< add to the fpu control word (after zeroing precision bits) to turn on extended precision \cite{INTEL}.
static const unsigned int STANDARD_PRECISION_BITS = 0x200;
///< add to the fpu control word (after zeroing precision bits) to turn off extended precision \cite{INTEL}.
void set_fpu_control_word(unsigned int mode)
{
asm ("fldcw %0" : : "m" (*&mode));
}
unsigned int get_fpu_control_word()
{
volatile unsigned int mode = 0;
asm ("fstcw %0" : "=m" (*&mode));
return mode;
}
bool fpu_set_extended_precision_is_on(bool state)
{
unsigned int old_cw = get_fpu_control_word();
unsigned int masked = old_cw & ~PRECISION_BIT_MASK;
unsigned int new_cw;
if(state)
new_cw = masked + EXTENDED_PRECISION_BITS;
else
new_cw = masked + STANDARD_PRECISION_BITS;
set_fpu_control_word(new_cw);
return true;
}
bool fpu_get_extended_precision_is_on()
{
unsigned int old_cw = get_fpu_control_word();
return ((old_cw & PRECISION_BIT_MASK) == 0x300);
}
Или вы можете просто запустить свой код с помощью valgrind, который не имитирует 80-битные регистры и, вероятно, проще для такой короткой программы!
Ширина регистров с плавающей запятой в x86 отличается от ширины double
в ОЗУ. Поэтому сравнения могут быть успешными или неудачными в зависимости от того, как компилятор решит оптимизировать загрузку значений с плавающей запятой.
Проблема, вероятно, является результатом потери некоторой точности при сохранении результата выражение по сравнению с компилятором, который не делает этого локально в качестве оптимизации:
double d = v[i % 4] * i; // the result, `d`, might be kept in a register
// instead of storing it in a memory location,
// keeping full precision
if(lst[i] != d) { // the value stored in lst[i] may have lost some
// precision since it had to be stored in memory,
// which might not be able to hold the full
// precision that the expression generated
Стандарт C99 говорит в 6.3.1.8/2 «Обычные арифметические преобразования»:
Значения операндов с плавающей запятой и результатов выражений с плавающей запятой могут быть представленным с большей точностью и диапазоном, чем требуется типом; типы не при этом изменяются.