After seeing how double.Nan == double.NaN
is always false in C#, I became curious how the equality was implemented under the hood. So I used Resharper to decompile the Double struct, and here is what I found:
public struct Double : IComparable, IFormattable, IConvertible, IComparable<double>, IEquatable<double>
{
// stuff removed...
public const double NaN = double.NaN;
// more stuff removed...
}
This seems to indicate the the struct Double
declares a constant that is defined in terms of this special lower case double
, though I'd always thought that the two were completely synonymous. What's more, if I Go To Implementation on the lowercase double, Resharper simply scrolls me to the declaration at the top of the file. Similarly, jumping to implementation of the lowercase's NaN
just takes me to the constant declaration earlier in the line!
So I'm trying to understand this seemingly recursive definition. Is this just an artefact of the decompiler? Perhaps a limitation in Resharper? Or is this lowercase double actually a different beast altogether - representing something at a lower level from the CLR/CTS?
Where does NaN
really come from?
Beware looking at decompiled code, especially if it is for something inbuilt. The actual IL here (for .NET 4.5, at least) is:
i.e. this is handled directly in IL via the
NaN
token.However, because it is a
const
(literal
in IL), it will get "burned into" the call site; anywhere else that usesdouble.NaN
will also be usingfloat64(NaN)
. Similarly, example, if I do:both of these assignments will look identical in the final IL (they will both be
ldc.i4.2
).Because of this, most decompilers will recognise the IL pattern
NaN
and represent it with the language's equivalent ofdouble.NaN
. But that doesn't mean that the code is itself recursive; they probably just don't have a check for "but is it double.NaN itself?". Ultimately, this is simply a special case, wherefloat64(NaN)
is a recognised value in IL.Incidentally, reflector decompiles it as:
That again doesn't meant that this is truth :p Merely that this is something which may have the same end result.
By far the best source you can get for .NET assemblies is the actual source code that was used to build them. Beats any decompiler for accuracy, the comments can be quite useful as well. Download the Reference Source.
You'll then also see that
Double.NaN
isn't defined in IL as Marc assumed, it's actually in a C# source code file. Thenet/clr/bcl/system/double.cs
source code file shows the real declaration:Which takes advantage of the C# compiler evaluating constant expressions at compile time. Or to put it tongue-in-cheek, NaN is defined by the C++ compiler since that's the language that was used to write the C# compiler ;)