Je cherchais une approche efficace pour calculer un b (disons a = 2
et b = 50
). Pour commencer, j'ai décidé de jeter un œil à l'implémentation de la Math.Pow()
fonction. Mais dans .NET Reflector , je n'ai trouvé que ceci:
[MethodImpl(MethodImplOptions.InternalCall), SecuritySafeCritical]
public static extern double Pow(double x, double y);
Quelles sont les ressources sur lesquelles je peux voir ce qui se passe à l'intérieur lorsque j'appelle Math.Pow()
fonction?
InternalCall
avec unextern
modificateur (car ils semblent être en conflit), veuillez voir la question (et les réponses qui en résultent) que j'ai postée à propos de cette même chose.2^x
opération ifx
est entier, le résultat est une opération de décalage. Alors peut-être pourriez-vous construire le résultat en utilisant une mantisse de2
et un exposant dex
.Réponses:
Cela signifie que la méthode est réellement implémentée dans le CLR, écrite en C ++. Le compilateur juste à temps consulte une table avec des méthodes implémentées en interne et compile l'appel à la fonction C ++ directement.
L'examen du code nécessite le code source du CLR. Vous pouvez l'obtenir auprès de la distribution SSCLI20 . Il a été écrit autour de la période de temps .NET 2.0, j'ai trouvé les implémentations de bas niveau, comme
Math.Pow()
étant encore largement précises pour les versions ultérieures du CLR.La table de recherche se trouve dans clr / src / vm / ecall.cpp. La section qui est pertinente
Math.Pow()
ressemble à ceci:La recherche de "COMDouble" vous amène à clr / src / classlibnative / float / comfloat.cpp. Je vais vous épargner le code, jetez un œ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 qui est intéressant est la macro FCIntrinsic dans le tableau. C'est un indice que la gigue peut implémenter la fonction comme 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
Pow()
, il n'y a aucune instruction FPU pour cela. Mais certainement pour les autres opérations simples. Il est à noter que cela peut rendre les mathématiques à virgule flottante en C # beaucoup plus rapides que le même code en C ++, vérifiez cette réponse pour la raison.Par ailleurs, le code source du CRT est également disponible si vous disposez de la version complète du répertoire Visual Studio vc / crt / src. Vous allez heurter le mur
pow()
, Microsoft a acheté ce code auprès 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 lorsque je l'ai essayé:Mais ce n'est pas un vrai substitut car il accumule les erreurs de 3 opérations en virgule flottante et ne traite pas les problèmes de domaine bizarre de Pow (). Comme 0 ^ 0 et -Infinity élevé à n'importe quelle puissance.
la source
pow
est notoirement difficile à mettre en œuvre avec précision, étant une fonction transcendantale (voir le dilemme de Table-Maker ). C'est beaucoup plus facile avec une puissance intégrale.La réponse de Hans Passant est excellente, mais je voudrais ajouter que si
b
est un entier, alors ila^b
peut être calculé très efficacement avec une décomposition binaire. Voici une version modifiée de Hacker's Delight de Henry Warren :Il note que cette opération est optimale (fait le nombre minimum d'opérations arithmétiques ou logiques) pour tous les b <15. De plus, il n'y a pas de solution connue au problème général de trouver une séquence optimale de facteurs à calculer
a^b
pour tout b autre qu'un vaste chercher. C'est un problème NP-Hard. Donc, fondamentalement, cela signifie que la décomposition binaire est aussi bonne que possible.la source
a
s'agit d'un nombre à virgule flottante.a
être un entier, mais le code le fait. En conséquence, je m'interroge sur la précision du résultat du calcul "très efficace" du texte.Si la version C disponible gratuitement
pow
est une indication, elle ne ressemble à rien de ce que vous attendez. Il ne vous serait pas très utile de trouver la version .NET, car le problème que vous résolvez (c'est-à-dire celui avec des entiers) est de l'ordre de grandeur plus simple et peut être résolu en quelques lignes de code C # avec l'exponentiation par l'algorithme de quadrature .la source