Math.Pow()は.NETフレームワークでどのように実装されていますか?
質問
私は計算するための効率的なアプローチを探していましたb (いう a = 2
と b = 50
)。物事を始めるために、私はの実装を見てみることにしました Math.Pow()
関数。しかし、 .NETリフレクター, 、私が見つけたのはこれだけでした:
[MethodImpl(MethodImplOptions.InternalCall), SecuritySafeCritical]
public static extern double Pow(double x, double y);
私が電話したときに何が起こっているのかを見ることができるリソースのいくつかは何ですか Math.Pow()
関数?
解決
MethodImplOptions.InternalCall
つまり、この方法は実際にCLRに実装されており、C ++で記述されています。 Just-in-Timeコンパイラは、内部的に実装されたメソッドを使用してテーブルを参照し、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になります。コードをspareしまないでしょう。自分を探してください。基本的にコーナーケースをチェックしてから、CRTのバージョンを呼び出します pow()
.
興味深いその他の唯一の実装の詳細は、テーブル内のfcintringicマクロです。これは、ジッターが関数を本質的に実装する可能性があるというヒントです。言い換えれば、フローティングポイントマシンコード命令で関数呼び出しを置き換えます。そうではありません Pow()
, 、FPU命令はありません。しかし、確かに他の単純な操作のために。注目すべきは、これにより、C#でFloating Point MathをC ++の同じコードよりも大幅に高速にすることができることです。 この答え 理由のために。
ちなみに、Visual Studio VC/CRT/SRCディレクトリのフルバージョンがある場合、CRTのソースコードも利用できます。壁にぶつかります pow()
ただし、MicrosoftはIntelからそのコードを購入しました。 Intelのエンジニアよりも良い仕事をすることはほとんどありません。私の高校の本のアイデンティティは、私がそれを試したとき、2倍の速さでしたが:
public static double FasterPow(double x, double y) {
return Math.Exp(y * Math.Log(x));
}
しかし、3つのフローティングポイント操作からエラーを蓄積し、POW()が持っている奇妙なドメインの問題に対処しないため、真の代替ではありません。 0^0と-Infinityのように、あらゆる電力に上げられます。
他のヒント
ハンス・パサントの答え 素晴らしいですが、私はそれを追加したい 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に対して最適である(算術または論理操作の最小数を実行する)ことを指摘しています。 a^b
大規模な検索以外のBの場合。それはNPハードの問題です。したがって、基本的には、バイナリ分解が得られるのと同じくらい良いことを意味します。
もしも 自由に利用可能なcバージョン pow
何らかの兆候であり、それはあなたが期待するもののようには見えません。 .NETバージョンを見つけるのはあまり役に立ちません。なぜなら、あなたが解決している問題(つまり、整数のあるもの)は桁違いに単純であり、C#コードの数行で解決できるため、 正方形アルゴリズムによる指数を使用して.