Question

Je cherchais une approche efficace pour calculer unb (dire a = 2 et b = 50). Pour commencer, j'ai décidé de jeter un œil à la mise en œuvre de Math.Pow() fonction. Mais en .NET Reflector, tout ce que j'ai trouvé, c'est ceci:

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

Quelles sont certaines des ressources où je peux voir comme ce qui se passe à l'intérieur quand j'appelle Math.Pow() fonction?

Était-ce utile?

La solution

MethodImplOptions.InternalCall

Cela signifie que la méthode est réellement implémentée dans le CLR, écrite en C ++. Le compilateur juste à temps consulte un tableau avec des méthodes implémentées en interne et compile directement l'appel à la fonction C ++.

Avoir un aperçu du code nécessite le code source du CLR. Vous pouvez obtenir cela à partir du Distribution SSCLI20. Il a été écrit autour du délai .NET 2.0, j'ai trouvé les implémentations de bas niveau, comme Math.Pow() être encore largement précis pour les versions ultérieures de la CLR.

La table de recherche est située dans CLR / SRC / VM / ECALL.CPP. La section qui est pertinente pour Math.Pow() ressemble à ça:

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 emmène à CLR / SRC / CLASSLIBNATIF / FLOOT / COMFLOAT.CPP. Je vais vous épargner le code, je viens de jeter un coup d'œil par vous-même. Il vérifie essentiellement les cas d'angle, puis appelle la version du CRT de pow().

Le seul autre détail d'implémentation intéressant est la macro Fcintrinsic dans le tableau. C'est un indice que la gigue peut implémenter la fonction comme un intrinsèque. En d'autres termes, remplacez l'appel de la fonction par une instruction de code machine à point flottant. Ce qui n'est pas le cas pour Pow(), il n'y a pas d'instructions FPU pour cela. Mais certainement pour les autres opérations simples. Il convient de noter que cela peut rendre les mathématiques à point flottant en C # sensiblement plus rapidement que le même code en C ++, vérifiez Cette réponse pour la raison.

Soit dit en passant, le code source du CRT est également disponible si vous disposez de la version complète du répertoire Visual Studio VC / CRT / SRC. Tu t'appuyeras sur le mur pow() Cependant, Microsoft a acheté ce code à Intel. Faire un meilleur travail que les ingénieurs Intel est peu probable. Bien que l'identité de mon livre du lycée ait été deux fois plus rapide lorsque je l'ai essayé:

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

Mais pas un véritable substitut car il accumule l'erreur à partir de 3 opérations de points flottants et ne traite pas des problèmes de domaine bizarre que possède POW (). Comme 0 ^ 0 et -Infinity levé à n'importe quelle puissance.

Autres conseils

La réponse de Hans Passant est génial, mais je voudrais ajouter que si b est un entier, alors a^b Peut être calculé très efficacement avec une décomposition binaire. Voici une version modifiée de Henry Warren Le plaisir des hackers:

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 tous les B <15. Il n'y a pas non plus de solution connue au problème général de la recherche d'une séquence optimale de facteurs à calculer a^b pour tout b autre qu'une recherche approfondie. C'est un problème NP-durs. Donc, fondamentalement, cela signifie que la décomposition binaire est aussi bonne que possible.

Si Version C librement disponible de pow est une indication, cela ne ressemble à rien de ce à quoi vous vous attendez. Il ne vous aidera pas beaucoup de choses à trouver la version .NET, car le problème que vous résolvez (c'est-à-dire celui avec des entiers) est des ordres de magnitudes plus simples, et peut être résolu en quelques lignes de code C # avec l'algorithme d'exponentiation par carré.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top