C # NaN comparative differences between Equals () and ==

Check this:

var a = Double.NaN; Console.WriteLine(a == a); Console.ReadKey(); 

Print "False"

  var a = Double.NaN; Console.WriteLine(a.Equals(a)); Console.ReadKey(); 

Prints "True"!

Why does he print "True"? Due to the specification of floating point numbers, the value of NaN is not equal to itself! So it seems that the Equals () method is implemented incorrectly ... Am I missing something?

+15
equals c # nan
Feb 08 2018-11-11T00:
source share
5 answers

I found an article dedicated to 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 NaN values ​​are never equal. However, according to the specification of the System.Object :: Equals method, it is advisable to override this method to provide the semantics of equality of value. [...]

So now we have two conflicting ideas about what Equality should mean. Object :: Equals says that BCL value types must be redefined to ensure equality, and IEC 60559 says that NaN is not equal to NaN. Section I of the ECMA specification provides a resolution for this conflict by taking note of this particular case in Section 8.2.5.2 [below]




Update:. The full text of section 8.2.5 of the CLI specification (ECMA-335) sheds some more light on this. I copied the corresponding bits here:

8.2.5 Identity and equality of values

There are two binary operators for all pairs of values: identity and equality. They return a logical result and are mathematical equivalence operators; that is, they:

  • Reflective - a op a is true.
  • Symmetric - a op b is true if and only if b op a true.
  • Transitive - if a op b true and b op c is true, then a op c true.

Moreover, although identification always means equality, the opposite is not true. [...]

8.2.5.1 Identification

The identical 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 the value type, then they are identical if and only if the bit sequences of the value coincide in different ways.
  • 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 using 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 must comply with the following rules:

  • Equality must be an equivalence operator, as defined above.
  • Identity should imply equality, as indicated earlier.
  • If the operand (or both) is a short value, [...]

Equality is implemented on System.Object through the Equals Method.

[Note: although two floating-point NaNs defined in IEC 60559: 1989 are always compared as unequal, the contract for System.Object.Equals requires that overrides satisfy operator equivalence requirements. Therefore, System.Double.Equals and System.Single.Equals return True when comparing two NaNs, while the equality operator returns False in this case, as required by the IEC standard. end note]

The above does not indicate the properties of the == operator at all (except for the last note); this primarily defines the behavior of ReferenceEquals and Equals . For the == operator behavior, the C # language specification (ECMA-334) (section 14.9.2) is clear about how to treat NaN Values:

If any of the operands [before operator == ] is NaN, the result will be false

+14
Feb 08 '11 at 2:35 a.m.
source share

Equals is done for things like hashtables. Therefore, the contract requires a.Equals(a) .

MSDN Status:

The following statements should be true for all implementations of the Equals method. In the list, x, y, and z represent references to objects that are not null.

x.Equals (x) returns true, with the exception of 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 if the objects referenced by x and y are not modified.

x.Equals (null) returns false.

See GetHashCode for additional required actions related to the Equals method.

What I find strange is that it claims that "x.Equals (x) returns true, except when it comes to floating point types. See IEC 60559: 1989, Binary floating-point arithmetic. point for microprocessor systems. " but at the same time requires that NaN equals NaN. So why did they introduce this exception? Due to different NaN?

Similarly, when using IComparer<double> floating point standard must also be violated. Because IComparer requires consistent, complete ordering.

+8
Feb 08 2018-11-14T00:
source share

If I dared to suggest, perhaps this is due to the support of using double values ​​as keys in the dictionary.

If x.Equals(y) returned false for x = double.NaN and y = double.NaN , then you could have this code:

 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 most developers would find this behavior rather unintuitive. But it would be even more illogical:

 // This would output false! Console.WriteLine(dict.ContainsKey(x)); 

Essentially, with an Equals implementation that never returns true for a particular value, you will have a type that can provide keys with the following weird behavior:

  • Can be added to the dictionary an unlimited number of times.
  • Could not be detected using ContainsKey , and therefore ...
  • Can never be removed with Remove

Remember that Equals very closely related to GetHashCode for this reason (the C # compiler even warns you if you override one without the other) - most of why they exist is to make it easier to use types as hash table keys.

As I said, this is just an assumption.

+5
Feb 08 2018-11-11T00:
source share

As long as you are correct that NaN == NaN is false, double.Equals handles NaN differently in such a way that NaN.Equals(NaN) is true. Here is the implementation of the .NET 4 method from the reflector:

 public bool Equals(double obj) { return ((obj == this) || (IsNaN(obj) && IsNaN(this))); } 
+3
Feb 08 2018-11-11T00:
source share

Check out this link for more information on when to use == or Equals . Written by illustrious leader John Skeet.

+1
Feb 08 '11 at 13:40
source share



All Articles