C# Math.Pow() 在 .NET Framework 中是如何实现的?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/8870442/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How is Math.Pow() implemented in .NET Framework?
提问by Pawan Mishra
I was looking for an efficient approach for calculating ab(say a = 2and 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:
我正在寻找一种有效的方法来计算 a b(比如说a = 2和b = 50)。首先,我决定看一下Math.Pow()函数的实现。但是在.NET Reflector 中,我发现的是:
[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?
当我调用Math.Pow()函数时,我可以看到哪些资源内部发生了什么?
采纳答案by Hans Passant
MethodImplOptions.InternalCall
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.
这意味着该方法实际上是在 CLR 中实现的,是用 C++ 编写的。即时编译器参考一个包含内部实现方法的表,并直接编译对 C++ 函数的调用。
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.
查看代码需要 CLR 的源代码。您可以从SSCLI20 发行版中获得。它是围绕 .NET 2.0 时间框架编写的,我发现低级实现Math.Pow()对于更高版本的 CLR 仍然基本准确。
The lookup table is located in clr/src/vm/ecall.cpp. The section that's relevant to Math.Pow()looks like this:
查找表位于 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()
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().
搜索“COMDouble”会将您带到 clr/src/classlibnative/float/comfloat.cpp。我不给你代码,你自己看看吧。它基本上检查极端情况,然后调用 CRT 的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 answerfor the reason why.
唯一有趣的其他实现细节是表中的 FCIntrinsic 宏。这暗示抖动可能会将功能实现为内在函数。换句话说,用浮点机器代码指令代替函数调用。情况并非如此Pow(),没有针对它的 FPU 指令。但是对于其他简单的操作肯定是有的。值得注意的是,这可以使 C# 中的浮点数学比 C++ 中的相同代码快得多,请查看此答案以了解原因。
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:
顺便说一下,如果您有完整版的 Visual Studio vc/crt/src 目录,也可以使用 CRT 的源代码。pow()不过,您会碰壁,微软从英特尔购买了该代码。不太可能比英特尔工程师做得更好。虽然我的高中书的身份在我尝试的时候快了两倍:
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.
但不是真正的替代品,因为它累积了 3 个浮点运算的错误,并且不处理 Pow() 具有的怪异域问题。像 0^0 和 -Infinity 提升到任何幂。
回答by dasblinkenlight
If freely available C version of powis any indication, it does not look like anything you would expect. It would not be of much help to you to find the .NET version, because the problem that you are solving (i.e. the one with integers) is orders of magnitudes simpler, and can be solved in a few lines of C# code with the exponentiation by squaring algorithm.
如果免费提供的 C 版本pow有任何迹象,它看起来并不像您期望的那样。找到 .NET 版本对您没有多大帮助,因为您正在解决的问题(即整数问题)要简单几个数量级,并且可以用几行 C# 代码和幂运算来解决通过平方算法。
回答by Michael Graczyk
Hans Passant's answer is great, but I would like to add that if bis an integer, then a^bcan be computed very efficiently with binary decomposition. Here's a modified version from Henry Warren's Hacker's Delight:
Hans Passant 的回答很好,但我想补充一点,如果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;
}
}
He notes that this operation is optimal (does the minimum number of arithmetic or logical operations) for all b < 15. Also there is no known solution to the general problem of finding an optimal sequence of factors to compute a^bfor any b other than an extensive search. It's an NP-Hard problem. So basically that means that the binary decomposition is as good as it gets.
他指出,对于所有 b < 15,此操作是最佳的(进行最少数量的算术或逻辑运算)。此外,对于找到计算a^b任何 b的最佳因子序列的一般问题,除了广泛的搜索。这是一个 NP-Hard 问题。所以基本上这意味着二进制分解尽可能好。

