什么时候System.Double不是双倍的?

时间:2013-05-22 13:43:50

标签: c# clr

在C#中看到double.Nan == double.NaN总是如此错误之后,我开始好奇如何在引擎盖下实现平等。所以我使用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定义的常量,尽管我总是认为这两个是完全同义的。更重要的是,如果我使用小写双精度执行实现,Resharper只需将我滚动到文件顶部的声明。同样地,跳转到小写的NaN的实现只是让我在前面的行中进行常量声明!

所以我试图理解这个看似递归的定义。这只是反编译器的人工制品吗?可能是Resharper的限制?或者这个小写的双重实际上是一个完全不同的野兽 - 代表CLR / CTS较低级别的东西?

NaN真正来自哪里?

2 个答案:

答案 0 :(得分:15)

注意查看反编译代码,特别是如果它是针对内置的东西。这里的实际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来表示它。但这并不意味着代码本身是递归的;他们可能只是没有检查“但是它是双重的.NNN本身?”。最终,这只是一种特殊情况,其中float64(NaN)是IL中的公认值。

顺便提一句,反射器将其反编译为:

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

这再次并不意味着这是真理:p仅仅是这可能具有相同的最终结果。

答案 1 :(得分:8)

到目前为止,.NET程序集的最佳来源是用于构建它们的实际源代码。击败任何反编译器以获得准确性,评论也非常有用。下载Reference Source

然后你还会看到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#编译器的语言;)