c# - Math.Pow()如何在.NET Framework中实现?




(2)

我正在寻找一种计算b的有效方法(比如a = 2和b = 50)。 为了开始,我决定看看Math.Pow()函数的实现。 但在.NET Reflector中 ,我发现的只有这个:

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

当我调用Math.Pow()函数时,我可以看到里面发生了什么的一些资源是什么?


MethodImplOptions.InternalCall

这意味着该方法实际上是在用C ++编写的CLR中实现的。 即时编译器通过内部实现的方法查询表并将调用直接编译为C ++函数。

查看代码需要CLR的源代码。 你可以从SSCLI20发行版中获得 。 它是围绕.NET 2.0时间框架编写的,我发现像Math.Pow()这样的低级实现对于更高版本的CLR仍然基本上是精确的。

查找表位于clr / src / vm / ecall.cpp中。 与Math.Pow()相关的部分如下所示:

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()

搜索“COMDouble”会将您带到clr / src / classlibnative / float / comfloat.cpp。 我会让你知道代码,只需要看看你自己。 它基本上检查角落案件,然后调用CRT的pow()版本pow()

唯一有趣的其他实现细节是表中的FCIntrinsic宏。 这暗示着抖动可能实现了作为内在功能的功能。 换句话说,将函数调用替换为浮点机器代码指令。 Pow()不是这种情况,它没有FPU指令。 但对于其他简单的操作肯定是如此。 值得注意的是,这可以使C#中的浮点数运算速度明显快于C ++中的相同代码,请检查此答案的原因。

顺便说一下,如果您有Visual Studio vc / crt / src目录的完整版本,CRT的源代码也可用。 不过,你会碰到pow() ,微软从英特尔那里购买了这些代码。 做比英特尔工程师更好的工作是不太可能的。 虽然我试过的时候,我的高中图书的身份快了两倍:

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

但不是一个真正的替代品,因为它从3个浮点操作中累积了错误,并没有处理Pow()具有的奇怪域问题。 就像0 ^ 0和-Infinity提高到任何力量。


Hans Passant的答案很好,但我想补充一点,如果b是一个整数,那么可以用二元分解非常有效地计算a^b 。 这里是亨利沃伦黑客的喜悦的修改版本:

public static int iexp(int a, uint b) {
    int y = 1;

    while(true) {
        if ((b & 1) != 0) y = a*y;
        b = b >> 1;
        if (b == 0) return y;
        a *= a;
    }    
}

他指出,对于所有的b <15,这个操作是最优的(算术或逻辑运算的最小次数)。对于找到最优的因子序列来计算任何b其他因子的一般问题,还没有已知的解决方案比广泛的搜索。 这是一个NP难题。 所以基本上这意味着二元分解就像它得到的那样好。







pow