Сравнение Double.NaN с самим собой

Я застреваю, пытаясь выяснить, почему эти две операции возвращают разные значения:

  • Double.NaN == Double.NaN возвращает false
  • Double.NaN.Equals(Double.NaN) возвращает true

У меня есть ответ на первую часть, но не на вторую, а не на "почему эти два сравнения возвращают разные значения"

Ответ 1

Причина разницы проста, если не очевидна.

Если вы используете оператор равенства ==, вы используете тест IEEE для равенства.

Если вы используете метод Equals(object), то вам необходимо сохранить контракт object.Equals(object). Когда вы реализуете этот метод (и соответствующий метод GetHashCode), вы должны поддерживать этот контракт, который отличается от поведения IEEE.

Если контракт Equals не был оставлен в силе, поведение хэш-таблиц будет нарушено.

var map = new Dictionary<double,string>();
map[double.NaN] = "NaN";
var s = map[double.NaN];

Если !double.NaN.Equals(double.NaN), вы никогда не получите свое значение из словаря!

Если предыдущее предложение не имеет смысла, тогда поймите, что механика хеширования (используемая в Dictionary<T,U>, HashSet<T> и т.д.) широко использует методы object.Equals(object) и object.GetHashCode() и полагается на гарантии их поведение.

Ответ 2

В самом конце раздела примечаний Double.Equals вы найдете:

Если два значения Double.NaN проверяются на равенство, вызывая метод Equals, метод возвращает true. Однако, если два значения NaN проверяются на равенство с помощью оператора равенства, оператор возвращает false. Если вы хотите определить, является ли значение Double не числом (NaN), альтернативой является вызов метода IsNaN.

Ответ 3

если вы проверите Double.NaN;

    // Summary:
    //     Represents a value that is not a number (NaN). This field is constant.
    public const double NaN = 0.0 / 0.0;

первый возвращает false, поскольку NaN не представляет никакого числа.

Метод или оператор возвращает NaN, когда результат операции undefined. Например, результат деления нуля на ноль равен NaN

Второй возвращает true, поскольку NaN равенство реализуется явно в перегруженном методе equals.

из msdn double.equals:

Если два значения Double.NaN проверяются на равенство, вызывая равенство метод возвращает true. Однако, если проверены два значения NaN для равенства с помощью оператора равенства оператор возвращает ложный. Если вы хотите определить, не является ли значение Double число (NaN), альтернативой является вызов метода IsNaN.

Это делается delibaretly, чтобы соответствовать IEC 60559:1989;

Согласно IEC 60559: 1989, два числа с плавающей запятой со значениями NaN никогда не равны. Однако, согласно спецификации для System.Object:: Equals метод, желательно переопределить этот метод для обеспечения ценности семантика равенства. Поскольку System.ValueType предоставляет это функциональность с помощью Reflection, описание для Object.Equals указывает, что типы значений должны учитывать переопределение реализации ValueType по умолчанию для достижения производительности увеличение. Фактически, глядя на источник System.ValueType:: Equals (строка 36 из clr\src\BCL\System\ValueType.cs в SSCLI), есть даже комментарий команды CLR Perf к Эффект System.ValueType:: Равно не быстрый.

обратитесь к: http://blogs.msdn.com/b/shawnfa/archive/2004/07/19/187792.aspx

Ответ 4

Хорошо, Oded answer отлично, но я хочу что-то сказать;

Когда я декомпилирую метод Double.Equals(), это выглядит так:

public bool Equals(double obj)
{
    return ((obj == this) || (IsNaN(obj) && IsNaN(this)));
}

Итак, поскольку мы имеем это = Double.NaN и obj = Double.NaN

(IsNaN(obj)) and (IsNaN(this)) returns `true`.

В принципе, это возможно return ((obj == this) || true

который равен

return ((obj == this) true.