En examinant de manière efficace le calcul de p ^ q (exponentiation), où q est un entier et passe en revue les normes C ++ 98 et C ++ 11, j’ai remarqué qu’apparemment la surcharge std::pow(double, int)
été supprimée dans C ++ 11.
En C ++ 98 26.5 / 6, il a le double pow(double, int);
Signature.
En C ++ 11 26.8, tout ce que je pouvais trouver était des surcharges en prenant une paire de double
, float
, double
ou long double
, et une note explicite: dans le cas d’un mélange de types intégrale et double, la surcharge pow(double, double)
devrait être choisi.
Est-ce juste une clarification de l’intention précédente, ont-ils été ajoutés de manière incorrecte dans C ++ 98, ont-ils été effectivement supprimés dans C ++ 11, ou autre chose?
De toute évidence, la version pow(double, int)
offre une belle opportunité d’optimisation, il semble donc étrange qu’ils soient supprimés. Un compilateur serait-il toujours conforme aux normes pour fournir une surcharge aussi optimisée?
double pow(double, int);
n’a pas été retiré de la spécification. Il a simplement été reformulé. Il vit maintenant dans [c.math] / p11. La façon dont il est calculé est un détail d’implémentation. La seule signature C ++ 03 qui a été modifiée est la suivante:
float pow(float, int);
Cela retourne maintenant le double:
double pow(float, int);
Et ce changement a été fait pour la compatibilité C.
Clarification :
26.8 [cmath] / p11 dit:
De plus, il doit y avoir des surcharges supplémentaires suffisantes pour assurer:
Si un argument correspondant à un paramètre double a un type long double, alors tous les arguments correspondant aux parameters doubles sont effectivement convertis en long double.
Sinon, si un argument correspondant à un paramètre double a le type double ou un type entier, tous les arguments correspondant aux parameters doubles sont effectivement convertis en double.
Sinon, tous les arguments correspondant aux parameters doubles sont effectivement convertis en éléments flottants.
Ce paragraphe implique toute une série de surcharges, notamment:
double pow(double, int); double pow(double, unsigned); double pow(double, unsigned long long);
etc.
Celles-ci peuvent être des surcharges réelles ou peuvent être implémentées avec des modèles restreints. Je l’ai personnellement mis en œuvre dans les deux sens et je suis fortement en faveur de la mise en œuvre de modèles restreints.
Deuxième mise à jour pour résoudre les problèmes d’optimisation:
L’implémentation est autorisée à optimiser toute surcharge. Mais rappelez-vous qu’une optimisation ne devrait être que cela. La version optimisée devrait renvoyer la même réponse. L’expérience des implémenteurs de fonctions telles que pow signifie qu’au moment où l’on se donne la peine de s’assurer que votre implémentation prend un exposant intégral, la “optimisation” est souvent plus lente.
En démonstration, le programme suivant imprime pow(.1, 20)
deux fois, une fois en utilisant std :: pow, et la deuxième fois en utilisant un algorithme “optimisé” tirant parti de l’exposant intégral:
#include #include #include int main() { std::cout << std::setprecision(17) << std::pow(.1, 20) << '\n'; double x = .1; double x2 = x * x; double x4 = x2 * x2; double x8 = x4 * x4; double x16 = x8 * x8; double x20 = x16 * x4; std::cout << x20 << '\n'; }
Sur mon système cela imprime:
1.0000000000000011e-20 1.0000000000000022e-20
Ou en notation hexadécimale:
0x1.79ca10c92422bp-67 0x1.79ca10c924232p-67
Et oui, les développeurs de pow s'inquiètent vraiment de tous ces bits en bas.
Donc, bien que la liberté soit de mélanger pow(double, int)
avec un algorithme séparé, la plupart des implémenteurs que je connais ont abandonné cette stratégie, à l'exception peut-être de la vérification de très petits exposants intégraux. Et dans ce cas, il est généralement avantageux de mettre cette vérification dans l'implémentation avec l'exposant en virgule flottante afin d'obtenir le meilleur rendement pour votre optimisation d'optimisation.