2013-05-22 10 views
13

Po zobaczeniu, jak double.Nan == double.NaN jest zawsze fałszywa w C#, zacząłem się zastanawiać, w jaki sposób wprowadzono równość pod maską. Więc użyłem ReSharper dekompilować podwójne struct, i oto co znalazłem:Kiedy system nie jest podwójny?

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

    public const double NaN = double.NaN; 

    // more stuff removed... 
} 

Wydaje się to wskazywać struct Double deklaruje stałą zdefiniowaną w kategoriach tego szczególnego małymi literami double, chociaż ja” d zawsze myślał, że te dwie są całkowicie synonimiczne. Co więcej, jeśli przejdę do implementacji małymi literami, Resharper po prostu przewinie mnie do deklaracji na górze pliku. Podobnie, przejście do implementacji małej litery NaN po prostu prowadzi mnie do stałej deklaracji wcześniej w linii!

Próbuję zrozumieć tę pozornie rekursywną definicję. Czy to tylko artefakt dekompilatora? Być może ograniczenie w Resharperze? Czy może to podwójne dno jest zupełnie inną bestią - reprezentującą coś na niższym poziomie od CLR/CTS?

Skąd naprawdę pochodzi NaN?

+2

Czy to jest powiązane? http://stackoverflow.com/questions/4751885/how-are-the-primitive-types-defined-non-recursively A także http://stackoverflow.com/questions/16113850/if-int32-is-just-an -alias-for-int-how-can-the-int32-class-use-an-int –

+3

Po prostu używając VS, aby wyświetlić metadane pokazuje 'public const double NaN = 0.0/0.0;' –

+1

'NaN' oznacza' Not a Liczba' i może być dodatnia lub ujemna, podobnie jak 'Nieskończoność'. Na wypadek, gdyby ktoś się zastanawiał. – Nolonar

Odpowiedz

8

Zdecydowanie najlepszym źródłem dla zestawów .NET jest rzeczywisty kod źródłowy, który został użyty do ich zbudowania. Uderza jakikolwiek dekompilator pod względem dokładności, a komentarze mogą być bardzo przydatne. Pobierz Reference Source.

Zobaczysz także, że Double.NaN nie jest zdefiniowany w IL, jak zakładał Marc, w rzeczywistości jest to kod źródłowy C#.Plik kod net/clr/bcl/system/double.cs źródło przedstawia deklarację prawdziwy:

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

który wykorzystuje kompilatora C# oceniającego stałych wyrażeń w czasie kompilacji. Lub, mówiąc językiem "na poły", NaN jest definiowany przez kompilator C++, ponieważ jest to język używany do pisania kompilatora C#;)

15

Uwaga na kod dekompilowany, zwłaszcza jeśli dotyczy on czegoś wbudowanego. Rzeczywista IL tutaj (NET 4,5 przynajmniej) wynosi:

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

to znaczy jest to obsługiwane bezpośrednio IL przez NaN tokena.

Jednakże, ponieważ jest to const (literal w IL), zostanie "wypalony" w miejscu wywołania; nigdzie indziej, który używa double.NaN będzie również używać float64(NaN). Podobnie, w przykładzie, jeśli należy:

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

obu tych zadań będzie wyglądała w końcowym II (będą one być zarówno ldc.i4.2).

Z tego powodu większość dekompilatorów rozpozna wzór IL NaN i będzie go reprezentować odpowiednikiem języka double.NaN. Ale to nie znaczy, że kod jest sam w sobie rekurencyjny; prawdopodobnie po prostu nie mają czeku "ale czy to jest podwójne. Czy to samo?". Ostatecznie jest to po prostu szczególny przypadek, w którym float64(NaN) jest uznaną wartością w IL.

Nawiasem mówiąc, reflektor decompiles go jako:

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

To znowu nie oznaczało, że jest to prawda: P Tylko że to jest coś, co może mieć taki sam wynik końcowy.