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.
Итак, подведем итог, было несколько отдельных вопросов:
исходный пример кода дал неопределенное поведение, нарушив строгий псевдоним. Исправления этого было достаточно для решения проблемы для некоторых версий 32-битного компилятора.
с этой проблемой, устраняя /fp:fast
решил проблему для всех доступных мне версий 32-битных и 64-битных версий
Мартиньо упоминает, что проблема больше не существует в cl 16.0 даже с /fp:fast
Это потому, что функция QNaN вызывает UB, нарушая строгий псевдоним. Компилятор VS имеет все права на любое поведение.
Вы вызываете неопределенное поведение, бросая 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-битного компилятора нет.
Я полагаю, что вы ищете /fp:strict
вариант.
Вы также можете хотеть float_control
Прагма.
Все это было правильно в документации в разделе «Опции компилятора по категориям»