Math.Pow()是如何在.NET Framework中实现的?

时间:2012-01-15 14:31:47

标签: c# .net pow

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

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

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

4 个答案:

答案 0 :(得分:844)

  

MethodImplOptions.InternalCall

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

查看代码需要CLR的源代码。你可以从SSCLI20 distribution获得。它是围绕.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()

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

顺便说一下,如果你有完整版的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一样提升到任何力量。

答案 1 :(得分:105)

Hans Passant's answer 很棒,但我想补充一点,如果b是一个整数,那么可以使用二进制分解非常有效地计算a^b。这是Henry Warren的 Hacker's Delight 的修改版本:

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计算a^b的最佳因子序列的一般问题,也没有已知的解决方案。这是NP难问题。所以基本上这意味着二进制分解就像它一样好。

答案 2 :(得分:66)

如果freely available C version of pow有任何迹象,那么它看起来就像你期望的那样。找到.NET版本对你没有太大帮助,因为你正在解决的问题(即整数问题)是更简单的数量级,并且可以用几行C#代码来解决{{3 }}

答案 3 :(得分:0)

仔细研究答案,了解了很多有关幕后计算的知识: 我已经在具有广泛测试覆盖案例的编码平台上尝试了一些变通方法,并找到了一种非常有效的方法(解决方案3):

nav