什么是系统?双份不是双份

本文关键字:系统 什么 | 更新日期: 2023-09-27 18:15:02

看到double.Nan == double.NaN在c#中总是为假之后,我开始好奇这个等式是如何在底层实现的。所以我使用Resharper来反编译Double结构体,下面是我发现的:

public struct Double : IComparable, IFormattable, IConvertible, IComparable<double>, IEquatable<double>
{
    // stuff removed...
    public const double NaN = double.NaN;
    // more stuff removed...
}

这似乎表明结构体Double声明了一个用特殊的小写double定义的常数,尽管我一直认为这两个是完全同义的。更重要的是,如果我在小写double上Go To Implementation, Resharper只是将我滚动到文件顶部的声明。类似地,跳转到小写的NaN的实现只是把我带到了本行前面的常量声明!

所以我试图理解这个看似递归的定义。这只是反编译器的一个工件吗?也许是Resharper的限制?或者这个小写的double实际上是一个完全不同的东西——代表比CLR/CTS更低级别的东西?

NaN到底是从哪里来的?

什么是系统?双份不是双份

注意查看反编译代码,特别是针对某些内置的代码。这里的实际IL(至少对于。net 4.5)是:

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

。这是通过NaN令牌在IL中直接处理的。

然而,因为它是const (IL中的literal),它将被"烧毁"到呼叫站点;其他地方使用double.NaN也将使用float64(NaN)。类似地,例如,如果我这样做:

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

这两个赋值在最终IL中看起来是相同的(它们都将是ldc.i4.2)。

因此,大多数反编译器将识别IL模式NaN,并用语言的等效double.NaN表示它。但这并不意味着代码本身是递归的;他们可能只是没有支票,但它是双重的。南本身?"最终,这只是一个特殊情况,其中float64(NaN)在IL中是一个可识别的值。

顺便说一下,反射器反编译为:

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

这并不意味着这是真理,p仅仅意味着这可能有相同的最终结果

到目前为止,您可以获得的. net程序集的最佳源代码是用于构建它们的实际源代码。在准确性方面胜过任何反编译器,注释也非常有用。下载参考源代码。

你还会看到Double.NaN并没有像Marc假设的那样在IL中定义,它实际上是在c#源代码文件中。net/clr/bcl/system/double.cs源代码文件显示了真实的声明:

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

它利用了c#编译器在编译时计算常量表达式的优势。或者开玩笑地说,NaN是由c++编译器定义的,因为c++是用来编写c#编译器的语言;)