什么是系统?双份不是双份
本文关键字:系统 什么 | 更新日期: 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#编译器的语言;)