在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
真正来自哪里?
答案 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#编译器的语言;)