Comment est les Mathématiques.Pow () mis en œuvre in.NET cadre?
je cherchais une méthode efficace pour calculer un b (dire a = 2
et b = 50
). Pour commencer, j'ai décidé de jeter un oeil à la mise en œuvre de la fonction Math.Pow()
. Mais dans . réflecteur net , tout ce que j'ai trouvé était ceci:
[MethodImpl(MethodImplOptions.InternalCall), SecuritySafeCritical]
public static extern double Pow(double x, double y);
quelles sont certaines des ressources où je peux voir ce qui se passe à l'intérieur quand j'appelle Math.Pow()
fonction?
3 réponses
MethodImplOptions.InternalCall
cela signifie que la méthode est effectivement implémentée dans le CLR, écrit en C++. Le compilateur juste-à-temps consulte une table avec des méthodes implémentées à l'interne et compile l'appel à la fonction c++ directement.
pour consulter le code, il faut le code source du CLR. Vous pouvez obtenir cela de la distribution SSCLI20 . Il a été écrit autour du .NET 2.0 time frame, j'ai trouvé que les implémentations de bas niveau, comme Math.Pow()
sont encore largement exactes pour les versions ultérieures du CLR.
la table de recherche se trouve dans clr/src/vm/ecall.rpc. La section qui s'applique à Math.Pow()
ressemble à ceci:
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()
la recherche de" COMDouble " vous amène à clr/src/classlibnative/float/comfloat.rpc. Je vous épargne le code, regardez par vous-même. Il vérifie essentiellement pour les cas de coin, puis appelle la version de la CRT de pow()
.
le seul autre détail de mise en œuvre qui est intéressant est la macro FCIntrinsic dans le tableau. C'est un indice que la gigue peut mettre en œuvre la fonction intrinsèque. En d'autres termes, remplacez l'appel de fonction par une instruction de code machine à virgule flottante. Ce qui n'est pas le cas pour Pow()
, il n'y a pas D'instruction FPU pour cela. Mais, pour les autres opérations simples. Il est à noter que cela peut faire les mathématiques à virgule flottante en C # sensiblement plus rapide que le même code en C++, cochez cette réponse pour la raison pourquoi.
soit dit en passant, le code source pour le CRT est également disponible si vous avez la version complète du répertoire Visual Studio vc/crt/src. Vous allez frapper le mur sur pow()
cependant, Microsoft a acheté ce code D'Intel. Faire un meilleur travail que les ingénieurs Intel est peu probable. Bien que l'identité de mon livre de lycée était deux fois plus rapide quand je l'ai essayé:
public static double FasterPow(double x, double y) {
return Math.Exp(y * Math.Log(x));
}
mais pas un vrai substitut parce qu'il accumule l'erreur de 3 opérations à virgule flottante et ne traite pas les problèmes de domaine bizarre que Pow() A. Comme 0^0 et-L'infini élevé à n'importe quelle puissance.
la réponse de Hans Passant est grande, mais je voudrais ajouter que si b
est un entier, alors a^b
peut être calculé très efficacement avec la décomposition binaire. Voici une version modifiée du de 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;
}
}
, Il note que cette opération est optimale (le nombre minimum d'opérations arithmétiques ou logiques) pour tout b < 15. Il n'y a pas non plus de solution connue pour le problème général de trouver une séquence optimale de facteurs pour calculer les 151920920" pour tout b autre qu'une recherche approfondie. C'est un problème NP-Difficile. Donc, fondamentalement, cela signifie que la décomposition binaire est aussi bon qu'il obtient.
si version c librement disponible de pow
est n'importe quelle indication, il ne ressemble pas à quelque chose que vous attendriez. Il ne serait pas d'une grande aide pour vous de trouver la version .NET, parce que le problème que vous résolvez (c.-à-d. celui avec des entiers) est des ordres de grandeurs plus simples, et peut être résolu dans quelques lignes de C# code avec l'exponentiation par l'algorithme de quadrature .