Check this out :
var a = Double.NaN;
Console.WriteLine(a == a);
Console.ReadKey();
Prints "False"
var a = Double.NaN;
Console.WriteLine(a.Equals(a));
Console.ReadKey();
Prints "True"!
Why it prints "True"? Due to floating point numbers specification, value that is NaN is not equal to itself! So it seems that Equals() method is implemented wrong...
Am I missing something ?
I found an article addressing your question: .NET Security Blog: Why == and the Equals Method Return Different Results for Floating Point Values
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.
[...]
So now we have two conflicting ideas
of what Equals should mean.
Object::Equals says that the BCL value
types should override to provide value
equality, and IEC 60559 says that NaN
does not equal NaN. Partition I of
the ECMA spec provides resolution for
this conflict by making a note about
this specific case in section 8.2.5.2 [below]
Update: The full text of section 8.2.5 from the CLI spec (ECMA-335) sheds some more light on this. I've copied the relevant bits here:
8.2.5 Identity and equality of values
There are two binary operators defined
on all pairs of values: identity and
equality. They return a Boolean result, and are mathematical
equivalence operators; that is, they are:
- Reflexive –
a op a
is true.
- Symmetric –
a op b
is true if and only if b op a
is true.
- Transitive – if
a op b
is true and b op c
is true, then a op c
is
true.
In addition, while identity always
implies equality, the reverse is not
true. [...]
8.2.5.1 Identity
The identity operator is defined by the CTS as follows.
- If the values have different exact types, then they are not identical.
- Otherwise, if their exact type is a value type, then they are identical if
and only if the bit sequences of the
values are the same, bit by bit.
- Otherwise, if their exact type is a reference type, then they are
identical if and only if the locations
of the values are the same.
Identity is implemented on System.Object
via the ReferenceEquals
method.
8.2.5.2 Equality
For value types, the equality operator
is part of the definition of the exact
type. Definitions of equality should
obey the following rules:
- Equality should be an equivalence operator, as defined above.
- Identity should imply equality, as stated earlier.
- If either (or both) operand is a boxed value, [...]
Equality is implemented on
System.Object
via the Equals
method.
[Note: Although two floating point
NaNs are defined by IEC 60559:1989 to
always compare as unequal, the
contract for System.Object.Equals
requires that overrides must satisfy
the requirements for an equivalence
operator. Therefore,
System.Double.Equals
and
System.Single.Equals
return True
when comparing two NaNs, while the
equality operator returns False in
that case, as required by the IEC
standard. end note]
The above does not specify the properties of the ==
operator at all (except for the final note); it is primarily defining the behavior of ReferenceEquals
and Equals
. For the behavior of the ==
operator, the C# language spec (ECMA-334) (section 14.9.2) is clear about how to treat NaN values:
If either operand [to operator ==
] is NaN, the result is false
Equals
is made for things like hashtables. And thus it contract requires that a.Equals(a)
.
MSDN states:
The following statements must be true for all implementations of the Equals method. In the list, x, y, and z represent object references that are not null.
x.Equals(x) returns true, except in cases that involve floating-point types. See IEC 60559:1989, Binary Floating-point Arithmetic for Microprocessor Systems.
x.Equals(y) returns the same value as y.Equals(x).
x.Equals(y) returns true if both x and y are NaN.
If (x.Equals(y) && y.Equals(z)) returns true, then x.Equals(z) returns true.
Successive calls to x.Equals(y) return the same value as long as the objects referenced by x and y are not modified.
x.Equals(null) returns false.
See GetHashCode for additional required behaviors pertaining to the Equals method.
What I find strange is that it states "x.Equals(x) returns true, except in cases that involve floating-point types. See IEC 60559:1989, Binary Floating-point Arithmetic for Microprocessor Systems." but at the same time requires that NaN equals NaN. So why did they put that exception in? Because of different NaNs?
In a similar way when using anIComparer<double>
the floating-point standard must be violated too. Since IComparer
requires a consistent total ordering.
If I were to venture a guess, it might be that this is to support the use of double
values as keys in a dictionary.
If x.Equals(y)
returned false
for x = double.NaN
and y = double.NaN
, then you could have code like this:
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!");
I think the majority of developers would find this behavior rather unintuitive. But what would be even more intuitive would be this:
// This would output false!
Console.WriteLine(dict.ContainsKey(x));
Basically, with an implementation of Equals
that never returns true
for a certain value, what you would have is a type capable of providing keys with the following bizarre behavior:
- Could be added an unlimited number of times to a dictionary
- Could not be detected using
ContainsKey
, and therefore...
- Could never be removed using
Remove
Remember that Equals
is very closely related to GetHashCode
for this very reason (the C# compiler even warns you if you've overridden one without the other)—a big part of why they're there in the first place is to facilitate the use of types as hash table keys.
Like I said, it's just a guess.
While you're correct that NaN == NaN
is false, double.Equals
specially handles NaN
differently, in a way that NaN.Equals(NaN)
is true. Here's the .NET 4 implementation of the method from reflector:
public bool Equals(double obj)
{
return ((obj == this) || (IsNaN(obj) && IsNaN(this)));
}
Check out this link for more information on when to use ==
's or Equals
. Written by the illustrious leader Jon Skeet.