Comparing Double.NaN with itself
Asked Answered
C

5

42

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"

Cultism answered 22/1, 2013 at 12:45 Comment(9)
Might be a stupid comment, but I would say that in case 1, you are comparing values. And in case 2 you are comparing references.Knitter
@Knitter No, you’re comparing values in both cases – doubles aren’t references and you’re not even boxing since System.Double.Equals is overloaded.Aggappera
@spender OMFG I should read the question better! Thanks!Poetry
@Poetry Actually, I suspect that this might still be the correct reason. I would have guessed the same thing.Aggappera
@RB I imagine the issues are broadly similar though.Mutineer
The same stands for float.Audly
This has nothing to do with reference/value types. It's about semantics. If you override Equals, you have to maintain a contract. See my answer.Bullough
I'd like to add, since no other answer did, that NaN represents a mathematical concept, which you can arrive at in multiple ways (e.g., sqrt(-1), 0/0, etc.), testing its equivalence against a real number is mathematically invalid. However, if we were to consider NaN not a value, but a state of a double, then you might be able to make the hands waving statement that "a double object in the state of NaN is equal to another object in the state of NaN". For ==, c# chose the former, for Equals C# chose the latter.Superphosphate
Here's something funny: double.NaN.Equals(float.NaN) is true, but float.NaN.Equals(double.NaN) is false. Well... actually, it's not funny.Blanca
B
36

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.

Bullough answered 22/1, 2013 at 13:0 Comment(11)
Ah. Finally. :-) (I’d like very much to get a proper reference for the claim here but I believe this is the correct answer (the contract part definitely is), so +1).Aggappera
Yes I should point out that I don't have a reference. It's just the only reason that makes sense to me.Bullough
+1. Can you please provide a ref to IEEE test for equality? I couldn't find it anywhereCultism
For completeness, this is given in a note in ECMA-335 I.8.2.5.2 "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]"Geisler
Excellent reasoning. We don't link to references here, we create them.Amylene
Very good answer. There is however one addition worth mentioning. When we use double as key in a Dictionary<,>, also the GetHashCode() implementation is important. There are however in IEEE many different binary representations of NaN (and they come in two categories, quiet NaN and signaling NaN). The Equals of .NET considers them all to be equal. Therefore it is a requirement that GetHashCode agrees on all those. But it doesn't. See this (closed) thread started by my for details.Philipp
It is not always a desirable feature to be able to use Double.NaN as a unique key in a map. See research.swtch.com/randhash for an interesting discussion where NaN values are assigned random hash values.Riffle
@Zayenz: That's really horrid. Lookup-based collections only make sense for types with defined equivalence relations. If certain objects shouldn't be retrievable from a collection, why are they being stored there in the first place?Salmonoid
@Salmonoid Well, since one might want to use doubles as keys sometimes, it becomes necessary to handle NaN keys. Since NaN is defined to not be equal to itself, there is little else one could do. The suggested way of handling it avoids avoids linear time complexity for linked chain hash-tables where lots of NaNs are stored in a simple way. Whether NaN should be non-equal to itself, or whether doubles makes sense as keys in a collection is another question.Riffle
@Zayenz: If doubles are going to be used as keys, one should either wrap them or specify a comparator such that NaN compares equal to itself, or else modify wrap the collection in something that will not accept anything that doesn't compare equal to itself (depending upon semantics, such an item may be silently discarded or throw an exception). If one is really stuck with the code that operates on the collection, I suppose jinxing the hash function may be a clever way to patch things to kinda sorta work, but from a design standpoint it's really hideous.Salmonoid
@Zayenz: As for whether different kinds of NaN values should compare equal to each other, that's something of another issue. Personally, my philosophy is that proper "universal standard" for overriding Object.Equals should be to test whether objects should be considered indistinguishable; (Object)(0.0/0.0).Equals((Object)(0.0/0.0)) should be true, but (Object)(1.0/(1.0/0.0)).Equals((Object)(-1.0/(1.0/0.0)) should be false [positive and negative zero represent numerically-indistinguishable quantities, but do not behave as indistinguishable quantities when divided into zero].Salmonoid
C
10

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.

Cookson answered 22/1, 2013 at 12:50 Comment(8)
Thanks for the quick answer. Do you know why this difference?Cultism
This doesn't answer the "why" part of the question.Cloy
may be because as IsNan is a function it is a reference, and then the two references to the same funtion are EqualsAdamandeve
(child's voice) "Buy Whyyy?"Lewendal
extract from the below link: "If the current instance is a reference type, the Equals(Object) method tests for reference equality"Adamandeve
@tschmit007, this has nothing to do with reference/value types.Bullough
@DrewNoakes I see. And I make a new try as a response.Adamandeve
Documented and simple answer : +1Choose
O
3

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

Oatis answered 22/1, 2013 at 12:52 Comment(7)
The question was why the two methods yield different results. You didn’t answer that.Aggappera
I do believe I have answered; NaN is not defined, does not represent any number, it is undefined. Talking about equality is not possible; but equals method defines an equality between two NaN values.Oatis
Why is Equals defined like this? You are simply begging the question.Aggappera
have you read the further updates? if you have further questions I can answer. read the link I have posted in the end please before.Oatis
The linked article indeed contains the explanation. However, the quote you’ve pulled out isn’t relevant.Aggappera
"Represents a value" is the key here. IIRC, there are 6 bit patterns for NaN.Tinstone
@daryal: If the == operator is supposed to answer the question "should X be considered indistinguishable from Y", then the answer to that question should be true if X and Y are both NaN. I'm not really sure what useful question == can answer with the IEEE semantics, since the fact that two floating-point variables hold the same nominal value doesn't mean they both represent the same quantity--merely that the quantities represented are indistinguishable.Salmonoid
R
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.

Raddled answered 22/1, 2013 at 12:59 Comment(3)
And again this is merely begging the question. We already knew that Double.Equals has special treatment for NaN, the question is why.Aggappera
@KonradRudolph, I explain why in my answer. It's because of the semantic contract that must be upheld.Bullough
@Drew Yes, and that’s why yours is the only real answer here.Aggappera
P
0

I would avoid either of these operations. According to the Microsoft documentation for code analysis usage rules, rule CA2242 (Test for NaN correctly) says "To fix a violation of this rule and accurately determine whether a value represents System.Double.NaN, use System.Single.IsNaN or System.Double.IsNaN to test the value.". So I would suggest you use double.IsNaN(double.NaN) && double.IsNaN(double.NaN) which will always return true. Pointless example, but correct. I would suggest you avoid comparing doubles for equality at all. Round off errors can result in very small differences which nevertheless are enough to break equality. When comparing two doubles named a and b I would use something like: if( Math.Abs( a - b ) < 1e-20 ). Maybe this doesn't answer the question, but I think it needs to be said.

Primavera answered 1/12, 2022 at 11:11 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.