首页 文章

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

提问于
浏览
410

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

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

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

3 回答

  • 832

    MethodImplOptions.InternalCall

    这意味着该方法实际上是在CLR中实现的,用C语言编写 . 即时编译器使用内部实现的方法查询表,并直接编译对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 . 我是'll spare you the code, just have a look for yourself. It basically checks for corner cases, then calls the CRT'的 pow() 版本 .

    唯一的其他实现细节's interesting is the FCIntrinsic macro in the table. That'暗示抖动可以将函数实现为内在函数 . 换句话说,用浮点机器代码指令替换函数调用 . Pow() 不是这种情况,没有FPU指令 . 但肯定是其他简单的操作 . 值得注意的是,这可以使C#中的浮点数学运算速度明显快于C中的相同代码,因此检查this answer .

    顺便说一句,如果你有完整版本的Visual Studio vc / crt / src目录,也可以使用CRT的源代码 . 尽管如此,微软还是从英特尔那里购买了这些代码 . 不太可能比英特尔工程师做得更好 . 虽然我的高中书的身份是我尝试时的两倍:

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

    但不是真正的替代品,因为它累积了来自3个浮点运算的错误,并且没有处理Pow()所具有的怪异域问题 . 像0 ^ 0和-Infinity提升到任何功率 .

  • 104

    Hans Passant's answer很棒,但我想补充一点,如果 b 是一个整数,那么 a^b 可以通过二进制分解非常有效地计算 . 在这里's a modified version from Henry Warren' s 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之外的任何b,找到计算 a^b 的最佳因子序列的一般问题也没有已知的解决方案 . 广泛的搜索 . 这是NP难问题 . 所以基本上这意味着二进制分解和它一样好 .

  • 63

    如果freely available C version of pow是任何指示,它看起来不像你期望的任何东西 . 找到.NET版本对你没有太大帮助,因为你正在解决的问题(即整数问题)是更简单的数量级,并且可以用几行C#代码with the exponentiation by squaring algorithm来解决 .

相关问题