Comparing Double.NaN with itself

2019-03-17 03:23发布

问题:

I am stuck trying to find out why these two operations return different values:

  1. Double.NaN == Double.NaN returns false
  2. Double.NaN.Equals(Double.NaN) returns true

I have the answer to the first part but not the second and not to "why are these two comparisons returning different values"

回答1:

The reason for the difference is simple, if not obvious.

If you use the equality operator ==, then you're using the IEEE test for equality.

If you're using the Equals(object) method, then you have to maintain the contract of object.Equals(object). When you implement this method (and the corresponding GetHashCode method), you have to maintain that contract, which is different from the IEEE behaviour.

If the Equals contract was not upheld, then the behaviour of hash tables would break.

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

If !double.NaN.Equals(double.NaN), you'd never get your value out of the dictionary!

If the previous sentence does not make sense, then understand that the mechanics of hashing (used in Dictionary<T,U>, HashSet<T>, etc) use both the object.Equals(object) and object.GetHashCode() methods extensively, and rely upon guarantees of their behaviour.



回答2:

At the very bottom of the remarks section of Double.Equals, you will find:

If two Double.NaN values are tested for equality by calling the Equals method, the method returns true. However, if two NaN values are tested for equality by using the equality operator, the operator returns false. When you want to determine whether the value of a Double is not a number (NaN), an alternative is to call the IsNaN method.



回答3:

Well, Oded's answer is great but I want to say something;

When I decompile Double.Equals() method, it seems like this;

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

So since we have this = Double.NaN and obj = Double.NaN

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

So basicly it is could return ((obj == this) || true

which is equvalent to

return ((obj == this) is true.



回答4:

if you inspect Double.NaN;

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

the first one returns false as NaN is not representing any number.

A method or operator returns NaN when the result of an operation is undefined. For example, the result of dividing zero by zero is NaN

The second one returns true as NaN equality is implemented explicitly in the overloaded equals method.

from msdn double.equals:

If two Double.NaN values are tested for equality by calling the Equals method, the method returns true. However, if two NaN values are tested for equality by using the equality operator, the operator returns false. When you want to determine whether the value of a Double is not a number (NaN), an alternative is to call the IsNaN method.

This is done delibaretly to conform with IEC 60559:1989;

According to IEC 60559:1989, two floating point numbers with values of NaN are never equal.However, according to the specification for the System.Object::Equals method, it's desirable to override this method to provide value equality semantics. Since System.ValueType provides this functionality through the use of Reflection, the description for Object.Equals specifically says that value types should consider overriding the default ValueType implementation to gain a performance increase. In fact from looking at the source of System.ValueType::Equals (line 36 of clr\src\BCL\System\ValueType.cs in the SSCLI), there's even a comment from the CLR Perf team to the effect of System.ValueType::Equals not being fast.

refer to: http://blogs.msdn.com/b/shawnfa/archive/2004/07/19/187792.aspx



标签: c# .net double nan