在.NET框架如何Math.Pow()来实现?来实现、框架、NET、Pow

2023-09-02 11:29:52 作者:逆光·初夏

我一直在寻找一种有效的方法来计算一个 B (说= 2和b = 50)。要开始做事了,我决定去看看 Math.Pow的实现()功能。但在 .net反射,所有我发现是这样的:

  [MethodImpl(MethodImplOptions.InternalCall),SecuritySafeCritical]
公共静态外部双战俘(双X,双Y);
 

有哪些资源,其中我可以为正在发生的事情里面,当我称之为 Math.Pow()功能?

见 解决方案   

MethodImplOptions.InternalCall

这意味着,该方法在CLR实际执行,用C ++编写。刚刚即时编译器咨询表与内部实现的方法和编译调用C ++函数直接。

有一个看code要求源$ C ​​$ C为CLR。你可以从 SSCLI20分布。这是写在.NET 2.0的时间框架的时候,我已经找到了低级别的实现,如 Math.Pow()将在很大程度上仍然准确更高版本的CLR。

查找表位于CLR / src目录/ VM / ecall.cpp。本节的相关 Math.Pow()是这样的:

  FCFuncStart(gMathFuncs)
    FCIntrinsic(仙,COMDouble ::仙,CORINFO_INTRINSIC_Sin)
    FCIntrinsic(产地来源证,COMDouble :: COS,CORINFO_INTRINSIC_Cos)
    FCIntrinsic(的Sqrt,COMDouble ::的Sqrt,CORINFO_INTRINSIC_Sqrt)
    FCIntrinsic(圆,COMDouble ::回合,CORINFO_INTRINSIC_Round)
    FCIntrinsicSig(ABS,和放大器; gsig_SM_Flt_RetFlt,COMDouble :: AbsFlt,CORINFO_INTRINSIC_Abs)
    FCIntrinsicSig(ABS,和放大器; gsig_SM_Dbl_RetDbl,COMDouble :: AbsDbl,CORINFO_INTRINSIC_Abs)
    FCFuncElement(EXP,COMDouble :: EXP)
    FCFuncElement(战俘,COMDouble ::战俘)
    // 等等..
FCFuncEnd()
 

搜索COMDouble带你到CLR / src目录/ classlibnative /漂浮/ comfloat.cpp。我就不告诉你了code,只要看看自己。它主要检查角落的情况下,然后调用CRT的版本的POW()

其他唯一的实现细节,这是有趣的是FCIntrinsic宏在表中。就是这样的抖动可以实现的功能作为内在的提示。换句话说,替换用的浮点机器code指令的函数调用。这是不是这样战俘(),没有FPU指令的。但可以肯定的其他简单的操作。值得注意的是,这可以使浮点运算在C#大大快于相同的code在C ++中,检查this回答的原因。

顺便说一句,源$ C ​​$ C的CRT也可以,如果你有完整版本的Visual Studio VC / CRT / src目录中。你会撞到墙上 POW()不过,微软购自英特尔的code。这样做比英特尔的工程师一个更好的工作的可能性不大。虽然我的高中本书的身份是美国的两倍快,当我试了一下:

 公共静态双FasterPow(双X,双Y){
    返回Math.Exp(Y *将Math.log(X));
}
 
员工信息管理系统的设计

但不是一个真正的替代品,因为它积累误差从3浮点运算,并且不处理怪人领域的问题,战俘()了。如0 ^ 0和负无穷大提出的任何权力。

I was looking for an efficient approach for calculating ab (say a = 2 and b = 50). To start things up, I decided to take a look at the implementation of Math.Pow() function. But in .NET Reflector, all I found was this:

[MethodImpl(MethodImplOptions.InternalCall), SecuritySafeCritical]
public static extern double Pow(double x, double y);

What are some of the resources wherein I can see as what's going on inside when I call Math.Pow() function?

解决方案

MethodImplOptions.InternalCall

That means that the method is actually implemented in the CLR, written in C++. The just-in-time compiler consults a table with internally implemented methods and compiles the call to the C++ function directly.

Having a look at the code requires the source code for the CLR. You can get that from the SSCLI20 distribution. It was written around the .NET 2.0 time frame, I've found the low-level implementations, like Math.Pow() to be still largely accurate for later versions of the CLR.

The lookup table is located in clr/src/vm/ecall.cpp. The section that's relevant to Math.Pow() looks like this:

FCFuncStart(gMathFuncs)
    FCIntrinsic("Sin", COMDouble::Sin, CORINFO_INTRINSIC_Sin)
    FCIntrinsic("Cos", COMDouble::Cos, CORINFO_INTRINSIC_Cos)
    FCIntrinsic("Sqrt", COMDouble::Sqrt, CORINFO_INTRINSIC_Sqrt)
    FCIntrinsic("Round", COMDouble::Round, CORINFO_INTRINSIC_Round)
    FCIntrinsicSig("Abs", &gsig_SM_Flt_RetFlt, COMDouble::AbsFlt, CORINFO_INTRINSIC_Abs)
    FCIntrinsicSig("Abs", &gsig_SM_Dbl_RetDbl, COMDouble::AbsDbl, CORINFO_INTRINSIC_Abs)
    FCFuncElement("Exp", COMDouble::Exp)
    FCFuncElement("Pow", COMDouble::Pow)
    // etc..
FCFuncEnd()

Searching for "COMDouble" takes you to clr/src/classlibnative/float/comfloat.cpp. I'll spare you the code, just have a look for yourself. It basically checks for corner cases, then calls the CRT's version of pow().

The only other implementation detail that's interesting is the FCIntrinsic macro in the table. That's a hint that the jitter may implement the function as an intrinsic. In other words, substitute the function call with a floating point machine code instruction. Which is not the case for Pow(), there is no FPU instruction for it. But certainly for the other simple operations. Notable is that this can make floating point math in C# substantially faster than the same code in C++, check this answer for the reason why.

By the way, the source code for the CRT is also available if you have the full version of Visual Studio vc/crt/src directory. You'll hit the wall on pow() though, Microsoft purchased that code from Intel. Doing a better job than the Intel engineers is unlikely. Although my high-school book's identity was twice as fast when I tried it:

public static double FasterPow(double x, double y) {
    return Math.Exp(y * Math.Log(x));
}

But not a true substitute because it accumulates error from 3 floating point operations and doesn't deal with the weirdo domain problems that Pow() has. Like 0^0 and -Infinity raised to any power.