Проблема GCC с сырыми данными удваивает сравнения типов

У меня есть следующий бит кода, однако при компиляции его с 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(lst[i]) != static_cast(d)), и добавьте "-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.

8
задан 23 March 2010 в 06:48
поделиться

3 ответа

Тот факт, что результат зависит от настроек оптимизации, наводит на мысль, что это может быть ошибка расширенной точности 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-битные регистры и, вероятно, проще для такой короткой программы!

7
ответ дан 5 December 2019 в 12:09
поделиться

Ширина регистров с плавающей запятой в x86 отличается от ширины double в ОЗУ. Поэтому сравнения могут быть успешными или неудачными в зависимости от того, как компилятор решит оптимизировать загрузку значений с плавающей запятой.

3
ответ дан 5 December 2019 в 12:09
поделиться

Проблема, вероятно, является результатом потери некоторой точности при сохранении результата выражение по сравнению с компилятором, который не делает этого локально в качестве оптимизации:

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 «Обычные арифметические преобразования»:

Значения операндов с плавающей запятой и результатов выражений с плавающей запятой могут быть представленным с большей точностью и диапазоном, чем требуется типом; типы не при этом изменяются.

4
ответ дан 5 December 2019 в 12:09
поделиться
Другие вопросы по тегам:

Похожие вопросы: