Сравнение Чисел в .NET
Когда люди сравнивают числа, они обычно ожидают, что равенство означает одинаковые значения. Для чисел с плавающей точкой (Half, float и double) в .NET существуют два понятия равенства:
1. Семантика сравнения IEEE, используемая оператором
==,2. Семантика эквивалентности для объектов .NET, используемая методом
Equals.На первый взгляд это выглядит противоречиво. На практике каждый из них решает свою проблему.
Краткое сравнение
Для чисел Half, double и float важны следующие крайние случаи:
1. NaN и NaN
-
== = false-
Equals = true-
CompareTo = 02. +0.0 и -0.0
-
== = true-
Equals = true-
CompareTo = 03. +Infinity и +Infinity
-
== = true-
Equals = true-
CompareTo = 04. -Infinity и +Infinity
-
== = false-
Equals = false-
CompareTo < 05. NaN и конечное число
-
== = false, a != = true-
Equals = false-
NaN.CompareTo(x) < 0Оператор
== соответствует правилам сравнения чисел с плавающей запятой из IEEE 754 / IEC 60559. Equals разработан для поддержки контрактов равенства .NET, используемых коллекциями и словарями.Почему NaN == NaN ложно, а NaN.Equals(NaN) истинно?
NaN означает «Не число». Это специальное значение IEEE 754, используемое, когда операция не определена, например, деление на 0 (0.0 / 0.0).
IEEE 754 рассматривает NaN как неупорядоченное значение при сравнениях, т.е.:
- x == y ложно, если хотя бы одна сторона NaN;
- x < y, x > y, x <= y, x >= y ложно, если хотя бы одна сторона NaN;
- x != y истинно, если хотя бы одна сторона NaN;
Таким образом:
double x = double.NaN;
Console.WriteLine(x == x); // False
Console.WriteLine(x != x); // True
Console.WriteLine(x < 0); // False
Console.WriteLine(x >= 0); // False
Чтобы проверить, что значение является NaN, используйте API типа:
Half h = Half.NaN;
float f = float.NaN;
double d = double.NaN;
Console.WriteLine(Half.IsNaN(h)); // True
Console.WriteLine(float.IsNaN(f)); // True
Console.WriteLine(double.IsNaN(d)); // True
Но .NET также нуждается в понятии равенства, которое работает с контейнерами на основе хэшей. Если бы оператор Equals соответствовал стилю IEEE для NaN, это нарушило бы рефлексивность (
x.Equals(x) должно быть true), и ключи, содержащие NaN, вели бы себя некорректно в словарях/хэшсетах. Поэтому Half.Equals, Double.Equals и Single.Equals обрабатывают NaN особым образом и возвращают true, когда оба значения являются NaN.Окончание следует…
Источник: https://www.meziantou.net/number-comparison-in-dotnet-equals-and-ieee-754-edge-cases.htm