Оптимизации VC ++ ломают сравнения с NaN?

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

Образец ниже дает правильный F F F F F T как и ожидалось, при компиляции с использованием g ++ на всех уровнях оптимизации и при компиляции с использованием CL.exe VC ++ (32-разрядная версия 15.00.30729.01) без аргументов оптимизации или любой комбинации / Od, / fp: fast, / arch: SSE.

Однако при компиляции с / O1 или / O2 (и с любыми / без других аргументов оптимизации), T T F F F T результаты даже с указанием / Op.

64-битная версия CL.exe дает много вариантов — T T F F F T, T T T F F T, T T T F F F и т. д. — в зависимости от уровня оптимизации и от того, указан ли / fp: fast, но, как и в случае 32-битной версии, совместимое поведение представляется возможным только при отключенной оптимизации.

Я делаю очевидную ошибку? Есть ли способ заставить компилятор соответствовать стандартам здесь, не жертвуя всеми остальными оптимизациями?

#include <limits>
#include <stdio.h>

int main( int argc, char const ** argv )
{
float test = std::numeric_limits<float>::quiet_NaN();

printf( "%c %c %c %c %c %c\n",
(test < test) ? 'T' : 'F',
(test <= test) ? 'T' : 'F',
(test == test) ? 'T' : 'F',
(test > test) ? 'T' : 'F',
(test >= test) ? 'T' : 'F',
(test != test) ? 'T' : 'F'
);

return 0;
}

Пример build.cmd что воспроизводит проблему:

set "PATH=c:\program files (x86)\Microsoft Visual Studio 9.0\VC\bin\amd64;c:\program files (x86)\Microsoft Visual Studio 9.0\Common7;c:\program files (x86)\Microsoft Visual Studio 9.0\Common7\IDE"set "LIB=c:\program files (x86)\microsoft visual studio 9.0\vc\lib\x64;c:\program files\Microsoft SDKs\Windows\v6.0A\Lib\x64"cl test.cpp /fp:fast /Od /c /I "c:\program files (x86)\microsoft visual studio 9.0\vc\include"link "/LIBPATH:C:/Program Files (x86)/Microsoft Visual Studio 9.0/vc/lib/amd64" "/LIBPATH:C:\Program Files\Microsoft SDKs\Windows\v6.0A\/Lib/x64" /DEBUG /IGNORE:4199 /IGNORE:4221 /MACHINE:X64 /SUBSYSTEM:CONSOLE  test.obj
test

РЕДАКТИРОВАТЬ

Для записи использовался пример, изначально приведенный в вопросе.

inline float QNaN()
{
static int const QNaNValue = 0x7fc00000;
return *(reinterpret_cast<float const*>(&QNaNValue));
}

генерировать NaN; как отмечается в ряде комментариев и ответов, это неопределенное поведение, и замена его на std :: numeric_limits :: quiet_NaN () фактически устранила проблему для некоторых версий 32-битного CL.exe.

8

Решение

Итак, подведем итог, было несколько отдельных вопросов:

  • исходный пример кода дал неопределенное поведение, нарушив строгий псевдоним. Исправления этого было достаточно для решения проблемы для некоторых версий 32-битного компилятора.

  • с этой проблемой, устраняя /fp:fast решил проблему для всех доступных мне версий 32-битных и 64-битных версий

  • Мартиньо упоминает, что проблема больше не существует в cl 16.0 даже с /fp:fast

5

Другие решения

Это потому, что функция QNaN вызывает UB, нарушая строгий псевдоним. Компилятор VS имеет все права на любое поведение.

4

Вы вызываете неопределенное поведение, бросая int* в float*, Я пробовал ваш код с VS 2010, используя std::numeric_limits<float>::quiet_NaN() вместо броска, и это дало ожидаемый результат (все, кроме последнего были false) с /O2 а также /fp:fast,

ОБНОВИТЬ

Я скопировал ваш пересмотренный пример в VS 2010 и VS 2005. В обоих случаях 32-битный компилятор дает правильные результаты (F F F F F T), а у 64-битного компилятора нет.

4

Я полагаю, что вы ищете /fp:strict вариант.

Вы также можете хотеть float_control Прагма.

Все это было правильно в документации в разделе «Опции компилятора по категориям»

0
По вопросам рекламы [email protected]