Подтвердить что ты не робот

Что заставило бы операторы C/С++ <, <= и == возвращать true, если любой аргумент NaN?

Мое понимание правил сравнения с плавающей точкой IEEE-754 заключается в том, что все операторы сравнения, кроме !=, вернут false, если один или оба аргумента являются NaN, а оператор != вернет true. Я могу легко воспроизвести это поведение с помощью простого автономного теста:

for (int ii = 0; ii < 4; ++ii)
{
    float a = (ii & 1) != 0 ? NAN : 1.0f;
    float b = (ii & 2) != 0 ? NAN : 2.0f;
    #define TEST(OP) printf("%4.1f %2s %4.1f => %s\n", a, #OP, b, a OP b ? "true" : "false");
    TEST(<)
    TEST(>)
    TEST(<=)
    TEST(>=)
    TEST(==)
    TEST(!=)
}

Это отображает ожидаемые результаты: (NaN отформатирован как -1.$ в среде выполнения MSVC)

 1.0  <  2.0 => true
 1.0  >  2.0 => false
 1.0 <=  2.0 => true
 1.0 >=  2.0 => false
 1.0 ==  2.0 => false
 1.0 !=  2.0 => true
-1.$  <  2.0 => false
-1.$  >  2.0 => false
-1.$ <=  2.0 => false
-1.$ >=  2.0 => false
-1.$ ==  2.0 => false
-1.$ !=  2.0 => true
 1.0  < -1.$ => false
 1.0  > -1.$ => false
 1.0 <= -1.$ => false
 1.0 >= -1.$ => false
 1.0 == -1.$ => false
 1.0 != -1.$ => true
-1.$  < -1.$ => false
-1.$  > -1.$ => false
-1.$ <= -1.$ => false
-1.$ >= -1.$ => false
-1.$ == -1.$ => false
-1.$ != -1.$ => true

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

 1.0  <  2.0 => true
 1.0  >  2.0 => false
 1.0 <=  2.0 => true
 1.0 >=  2.0 => false
 1.0 ==  2.0 => false
 1.0 !=  2.0 => true
-1.$  <  2.0 => true
-1.$  >  2.0 => false
-1.$ <=  2.0 => true
-1.$ >=  2.0 => false
-1.$ ==  2.0 => true
-1.$ !=  2.0 => false
 1.0  < -1.$ => true
 1.0  > -1.$ => false
 1.0 <= -1.$ => true
 1.0 >= -1.$ => false
 1.0 == -1.$ => true
 1.0 != -1.$ => false
-1.$  < -1.$ => true
-1.$  > -1.$ => false
-1.$ <= -1.$ => true
-1.$ >= -1.$ => false
-1.$ == -1.$ => true
-1.$ != -1.$ => false

По какой-то причине операторы <, <= и == неожиданно возвращаются к истине, когда один или оба аргумента являются NaN. Кроме того, оператор != неожиданно возвращает false.

Это 64-разрядный код, созданный с помощью Visual Studio 2010, работающий на Intel Xeon E5-2650. Используя _mm_getcsr(), я подтвердил, что регистр CSR имеет одинаковое значение в обоих сценариях.

Что еще может повлиять на поведение математики с плавающей запятой, как это?

4b9b3361

Ответ 1

Это связано с параметром /fp:fast MSVC, который (среди прочего) позволяет компилятору выполнять сравнения без учета правильного поведения NaN в чтобы генерировать более быстрый код. Использование /fp:precise или /fp:strict вместо этого приводит к тому, что эти сравнения ведут себя так, как ожидалось, когда представлены аргументы NaN.