Есть ли "аномалии" сравнения с плавающей точкой? - PullRequest
6 голосов
/ 13 сентября 2010

Если я сравниваю два числа с плавающей точкой, есть ли случаи, когда a>=b не эквивалентно b<=a и !(a<b), или когда a==b не эквивалентно b==a и !(a!=b)?

Другими словами: всегда ли сравнения "симметричны", так что я могу получить тот же результат при сравнении, меняя операнды и отражая оператор? И всегда ли они «отрицательны», так что отрицание оператора (например, от > до <=) эквивалентно применению логического НЕ (!) к результату?

Ответы [ 5 ]

7 голосов
/ 14 сентября 2010

Предполагая, что IEEE-754 с плавающей точкой:

  • a >= b всегда эквивалентно b <= a. *
  • a >= b эквивалентно !(a < b), если только одинили оба из a или b являются NaN.
  • a == b всегда эквивалентно b == a. *
  • a == b эквивалентно !(a != b), если только один илиa или b - это NaN.

В более общем смысле: трихотомия не выполняется для чисел с плавающей запятой.Вместо этого, связанное свойство имеет место [IEEE-754 (1985) §5.7]:

Возможны четыре взаимоисключающих отношения: меньше, равно, больше, и неупорядочено.Последний случай возникает, когда хотя бы один операнд является NaN.Каждый NaN должен сравнивать неупорядоченное со всем, в том числе и с самим собой.

Обратите внимание, что на самом деле это не «аномалия», а следствие расширения арифметики до степени, которая пытаетсяподдерживать согласованность с реальной арифметикой, когда это возможно.

[*] true в абстрактной арифметике IEEE-754.При реальном использовании некоторые компиляторы могут приводить к нарушению этого в редких случаях в результате выполнения вычислений с повышенной точностью (MSVC, я смотрю на вас).Теперь, когда большинство вычислений с плавающей запятой в архитектуре Intel выполняется на SSE вместо x87, это не представляет особой проблемы (и это всегда было ошибкой с точки зрения IEEE-754, во всяком случае).

3 голосов
/ 13 сентября 2010

В Python, по крайней мере, a>=b не эквивалентен !(a<b), когда задействован NaN:

>>> a = float('nan')
>>> b = 0
>>> a >= b
False
>>> not (a < b)
True

Я бы предположил, что это также имеет место в большинстве других языков.

Еще одна вещь, которая может вас удивить, это то, что NaN даже не сравнивается с собой:

>>> a == a
False
3 голосов
/ 14 сентября 2010

Множество чисел с плавающей точкой IEEE-754 не упорядочено, поэтому некоторая реляционная и булева алгебра, с которой вы знакомы, больше не выполняется.Эта аномалия вызвана NaN, который не имеет порядка относительно любого другого значения в наборе, включая самого себя, поэтому все реляционные операторы возвращают false.Это именно то, что показал Марк Байерс.

Если вы исключите NaN, то теперь у вас есть упорядоченный набор, и предоставленные вами выражения всегда будут эквивалентны.Это включает в себя бесконечности и отрицательный ноль.

2 голосов
/ 14 сентября 2010

Помимо проблемы NaN, которая в некоторой степени аналогична NULL в SQL и отсутствующим значениям в SAS и других статистических пакетах, всегда существует проблема арифметической точности с плавающей запятой.Повторяющиеся значения в дробной части (например, 1/3) и иррациональные числа не могут быть точно представлены.Арифметика с плавающей точкой часто обрезает результаты из-за конечного предела точности.Чем более логично вы делаете со значением с плавающей запятой, тем больше ошибка, которая появляется.

Вероятно, наиболее полезным способом сравнения значений с плавающей запятой будет алгоритм:

  1. Если любое из значений равно NaN, все сравнения являются ложными, если только вы явно не проверяете для NaN.
  2. Если разница между двумя числами находится в пределах определенного «коэффициента неопределенности», считайте их равными.Коэффициент нечеткости - это ваше допуск к накопленной математической неточности.
  3. После сравнения нечеткого равенства, затем сравните для значения меньше или больше.

Обратите внимание, что сравнение для "<=" или«> =» имеет тот же риск, что и сравнение для точного равенства.

1 голос
/ 13 сентября 2010

Нет, не для любой разумной реализации с плавающей точкой: применяются базовая симметрия и логическая логика. Тем не менее, равенство в числах с плавающей запятой сложно в других отношениях. Есть очень немного случаев, когда тестирование a==b для поплавков является разумным.

...