13

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?

Mike Chamberlain
  • 39,692
  • 27
  • 110
  • 158
  • 2
    Is this related? http://stackoverflow.com/questions/4751885/how-are-the-primitive-types-defined-non-recursively And also http://stackoverflow.com/questions/16113850/if-int32-is-just-an-alias-for-int-how-can-the-int32-class-use-an-int – Matthew Watson May 22 '13 at 13:47
  • 3
    Just using VS to view the metadata shows `public const double NaN = 0.0 / 0.0;` – Joel Rondeau May 22 '13 at 13:47
  • 1
    `NaN` means `Not a Number` and can be positive or negative, just like `Infinity`. In case anyone is wondering. – Nolonar May 22 '13 at 13:53
  • 1
    Note that double.Nan.GetHashCode() is always -524288, and double.Nan.Equals(double.Nan) is always true. It looks like they just didn't override the == operator to match the other notions of equality. – Lawrence May 22 '13 at 13:59
  • 1
    And just to make this clear: `double` is **always** identical to `System.Double`. The former is a synonym of the latter. – Konrad Rudolph May 22 '13 at 14:06
  • 1
    @KonradRudolph `global::System.Double`, at least. But you can be evil... http://pastie.org/7943586 – Marc Gravell May 22 '13 at 14:12
  • 1
    The compiled code will ask the floating point unit (8087, SSE etc.) to perform the comparison. And the FPU uses the standard IEEE754 rules. – David Heffernan May 22 '13 at 16:16
  • 1
    @MatthewWatson While those questions might be _related_, they surely are different questions and none of the answers answers this question exactly. [Is the question an exact duplicate?](http://meta.stackexchange.com/a/10844/192172): **no**, then don't vote to close! – Daniel A.A. Pelsmaeker May 22 '13 at 21:31
  • @Virtlink I didn't vote to close. Why do you think I did? I was providing some related links, as I stated. – Matthew Watson May 22 '13 at 22:31
  • @MatthewWatson Sorry, the 'don't vote to close' was more directed to the 'general public' that used your references to mark this as an _exact_ duplicate, which it is not. – Daniel A.A. Pelsmaeker May 23 '13 at 14:49
  • @Virtlink Aye, I didn't think it was a duplicate; I was just trying to provide some additional information. – Matthew Watson May 23 '13 at 15:13

2 Answers2

15

Beware looking at decompiled code, especially if it is for something inbuilt. The actual IL here (for .NET 4.5, at least) is:

.field public static literal float64 NaN = float64(NaN)
{
    .custom instance void __DynamicallyInvokableAttribute::.ctor()
}

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 uses double.NaN will also be using float64(NaN). Similarly, example, if I do:

const int I = 2;
int i = I;
int j = 2;

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 of double.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, where float64(NaN) is a recognised value in IL.

Incidentally, reflector decompiles it as:

[__DynamicallyInvokable]
public const double NaN = (double) 1.0 / (double) 0.0;

That again doesn't meant that this is truth :p Merely that this is something which may have the same end result.

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
8

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. The net/clr/bcl/system/double.cs source code file shows the real declaration:

  public const double NaN = (double)0.0 / (double)0.0;

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 ;)

Soner Gönül
  • 97,193
  • 102
  • 206
  • 364
Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536