2013-05-22 3 views
13

Nachdem ich gesehen habe, wie double.Nan == double.NaN in C# immer falsch ist, wurde ich neugierig, wie die Gleichheit unter der Haube umgesetzt wurde. So habe ich ReSharper die Doppel-Struktur zu dekompilieren, und hier ist das, was ich gefunden habe:Wann ist ein System.Double kein Double?

public struct Double : IComparable, IFormattable, IConvertible, IComparable<double>, IEquatable<double> 
{ 
    // stuff removed... 

    public const double NaN = double.NaN; 

    // more stuff removed... 
} 

Dies scheint darauf hinzudeuten die die Struktur Double eine Konstante erklärt, die in Bezug auf diese spezielle Kleinschreibung definiert ist double, obwohl ich Ich dachte immer, dass die beiden auch komplett sind. Wenn ich die Implementation in der Kleinbuchstaben-Doppelfunktion verwende, scrollt Resharper einfach zu der Deklaration am Anfang der Datei. In ähnlicher Weise führt das Springen zur Implementierung des Kleinbuchstabens NaN mich nur zur konstanten Erklärung früher in der Linie!

Also versuche ich diese scheinbar rekursive Definition zu verstehen. Ist das nur ein Artefakt des Decompilers? Vielleicht eine Einschränkung in Resharper? Oder ist dieser Kleinbuchstabe tatsächlich ein ganz anderes Biest, das etwas von der CLR/CTS repräsentiert?

Woher kommt eigentlich NaN?

+2

Ist das verwandt? http://stackoverflow.com/questions/4751885/how-are-the-primitive-types-defined-non-recursively Und auch http://stackoverflow.com/questions/16113850/if-int32-is-just-an -alias-for-int-wie-kann-der-int32-class-use-an-int –

+3

Wenn Sie VS verwenden, um die Metadaten anzuzeigen, zeigt 'public const double NaN = 0.0/0.0;' –

+1

'NaN' bedeutet' Nicht a Number 'und kann positiv oder negativ sein, genau wie' Infinity '. Falls sich jemand fragt. – Nolonar

Antwort

8

Bei weitem die beste Quelle, die Sie für .NET-Assemblys erhalten können, ist der eigentliche Quellcode, mit dem sie erstellt wurden. Beats Decompiler für Genauigkeit, die Kommentare können auch sehr nützlich sein. Laden Sie die Reference Source herunter.

Sie werden dann auch sehen, dass Double.NaN nicht in IL definiert ist, wie Marc angenommen hat, es ist tatsächlich in einer C# Quellcodedatei.Die net/clr/bcl/system/double.cs Quellcodedatei zeigt die echten Erklärung:

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

Welchen Vorteil der C# -Compiler Auswertung konstante Ausdrücke bei der Kompilierung dauert. Oder, um es witzig zu sagen, NaN wird vom C++ - Compiler definiert, da das die Sprache ist, die zum Schreiben des C# -Compilers verwendet wurde;)

15

Vorsicht bei der Analyse von dekompiliertem Code, besonders wenn es etwas eingebautes ist. Die tatsächliche IL hier (für NET 4.5, mindestens) ist:

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

heißt dies direkt in IL über den NaN Token abgewickelt wird.

Da es jedoch ein const (literal in IL) ist, wird es in die Call-Site "eingebrannt" werden; an anderer Stelle, die double.NaN verwendet, wird auchfloat64(NaN) verwendet werden. Ähnlich Beispiel, wenn ich tun:

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

beide Zuordnungen identisch aussehen im Finale IL (sie werden beide ldc.i4.2 sein).

Aus diesem Grund werden die meisten Dekompiler das IL-Muster NaN erkennen und es mit dem entsprechenden Wert der Sprache double.NaN darstellen. Aber das bedeutet nicht, dass der Code selbst rekursiv ist; Sie haben wahrscheinlich einfach keinen Scheck für "aber ist es doppelt.NaN selbst?". Letztlich ist dies nur ein Sonderfall, bei dem float64(NaN) ein anerkannter Wert in IL ist.

Übrigens Reflektor dekompiliert es als:

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

das wieder nicht gemeint, daß diese Wahrheit ist: p lediglich, dass dies etwas ist, was das gleiche Endergebnis haben.