Мое понимание правил сравнения с плавающей точкой 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 имеет одинаковое значение в обоих сценариях.
Что еще может повлиять на поведение математики с плавающей запятой, как это?