c# NaN сравнение различий между Equals () и ==
зацени вот это :
var a = Double.NaN;
Console.WriteLine(a == a);
Console.ReadKey();
Печать "False"
var a = Double.NaN;
Console.WriteLine(a.Equals(a));
Console.ReadKey();
Печать "True"!
почему он печатает "True"? Из-за спецификации чисел с плавающей запятой значение, которое является NaN, не равно самому себе! Таким образом, кажется, что метод Equals() реализован неправильно... Я что-то упускаю ?
5 ответов
я нашел статью, касающуюся вашего вопроса:блог безопасности .NET: почему == и метод Equals возвращают разные результаты для значений с плавающей запятой
согласно IEC 60559: 1989, два числа с плавающей запятой со значениями Нэн никогда не равны. Однако, согласно спецификации для Система.Метод Object:: Equals, это желательно переопределить этот метод для обеспечить семантику равенства значений. [...]
так теперь у нас две противоречивые идеи о том, что должно означать равенство. Object:: Equals говорит, что значение BCL типы должны переопределять для предоставления значения равенство, и IEC 60559 говорит, что NaN не равно NaN. Раздел I из спецификация ECMA обеспечивает разрешение для этот конфликт, сделав заметку о этот конкретный случай в разделе 8.2.5.2 [ниже]
обновление: полный текст раздела 8.2.5 из спецификации CLI (ECMA-335) проливает еще немного света на это. Я скопировал соответствующие биты здесь:
8.2.5 идентичность и равенство ценностей
определены два двоичных оператора на всех парах значений:личность и равенство. Они возвращают логический результат, и являются математическими операторы эквивалентности; то есть, они являются:
- рефлексивно –
a op a
- Это правда.- симметричные –
a op b
true если и только еслиb op a
- это правда.- транзитивность – если
a op b
true иb op c
истинно, тоa op c
is истинный.кроме того, в то время как личность всегда подразумевает равенство, обратного нет истинный. [...]
8.2.5.1 удостоверение
оператор идентификации определяется CTS следующим образом.
- если значения имеют разные типы, то они не идентичный.
- в противном случае, если их точный тип является типом значения, то они идентичны, если и только если битовые последовательности значения одинаковы, шаг за шагом.
- в противном случае, если их точный тип является ссылочным типом, тогда они идентичные, если и только если местоположения значения такие же.
идентификатор реализован на
System.Object
черезReferenceEquals
метод.равенство 8.2.5.2
для типы значений, оператор равенства является частью определения точного тип. Определения равенства должны соблюдайте следующие правила:
- равенство должно быть оператором эквивалентности, как определено выше.
- идентичность должна подразумевать равенство, как было сказано ранее.
- если любой (или оба) операнд является коробочным значением, [...]
равенство реализуется на
System.Object
черезEquals
метод.[Примечание: хотя две плавающие точки NaNs определены IEC 60559: 1989 к всегда сравнивают как неравные, контракт на закупку System.Объект.Равняется требует, чтобы переопределения удовлетворяли требования к эквивалентности оператор. Следовательно,
System.Double.Equals
иSystem.Single.Equals
вернуть True при сравнении двух Нан, в то время как оператор равенства возвращает False in в этом случае, как того требует МЭК норматив. конец Примечания]
в выше не указаны свойства ==
оператор вообще (за исключением заключительной ноты); он в первую очередь определяет поведение ReferenceEquals
и Equals
. За поведение ==
оператор спецификация языка C# (ECMA-334) (раздел 14.9.2) ясно о том, как лечить значения NaN:
если любой операнд [to
operator ==
] является NaN, результат false
Equals
сделан для таких вещей, как хеш-таблицы. И поэтому контракт требует, чтобы a.Equals(a)
.
MSDN заявляет:
следующие операторы должны быть true для всех реализаций метода Equals. В списке x, y и z представляют ссылки на объекты, которые не являются нулевыми.
x.Equals (x) возвращает true, за исключением случаев, связанных с типами с плавающей запятой. См. МЭК 60559:1989 двоичная арифметика с плавающей точкой для микропроцессорных Системный.
x.Equals (y) возвращает то же значение, что и y.Равно (x).
x.Equals (y) возвращает true, если x и y равны NaN.
If (x.Равно(y) & & y.Equals (z)) возвращает true, затем x.Equals (z) возвращает true.
последовательные вызовы x.Equals (y) возвращает то же значение, пока объекты, на которые ссылаются x и y, не изменяются.
x.Equals (null) возвращает false.
дополнительные сведения см. В разделе GetHashCode требуемое поведение, относящееся к методу Equals.
что я нахожу странным, так это то, что в нем говорится "x".Equals (x) возвращает true, за исключением случаев, связанных с типами с плавающей запятой. См. IEC 60559: 1989, двоичная арифметика с плавающей запятой для микропроцессорных систем.- но в то же время требует, чтобы НАН равнялась НАН. Так почему же они сделали исключение? Из-за разных бабушек?
аналогичным образом при использованииIComparer<double>
стандарт с плавающей запятой должен быть нарушен тоже. С IComparer
требует последовательного полного заказа.
если бы я рискнул предположить, это может быть, что это для поддержки использования double
значения как ключи в словаре.
если x.Equals(y)
вернулся false
на x = double.NaN
и y = double.NaN
, тогда у вас может быть такой код:
var dict = new Dictionary<double, string>();
double x = double.NaN;
dict.Add(x, "These");
dict.Add(x, "have");
dict.Add(x, "duplicate");
dict.Add(x, "keys!");
Я думаю, что большинство разработчиков нашла бы такое поведение весьма нелогично. но что было бы даже больше интуитивным было бы следующее:
// This would output false!
Console.WriteLine(dict.ContainsKey(x));
в основном, с реализация Equals
это никогда возвращает true
для определенного значения у вас будет тип, способный предоставлять ключи со следующим странным поведением:
- можно добавить неограниченное количество раз в словарь
- мог бы не быть обнаружены с помощью
ContainsKey
, и поэтому... - невозможно удалить с помощью
Remove
помните, что Equals
очень тесно связано с GetHashCode
именно по этой причине (компилятор C# даже предупреждает вас, если вы переопределили один без другого) - большая часть того, почему они там в первую очередь, заключается в том, чтобы облегчить использование типов в качестве ключей хэш-таблицы.
как я уже сказал, это лишь предположение.
в то время как верно, что NaN == NaN
ложь, double.Equals
специально ручками NaN
по-другому, таким образом, что NaN.Equals(NaN)
- это правда. Вот реализация .NET 4 метода из reflector:
public bool Equals(double obj)
{
return ((obj == this) || (IsNaN(obj) && IsNaN(this)));
}
зацените ссылке для получения дополнительной информации о том, когда использовать ==
или Equals
. Написано знаменитым лидером Джоном скитом.