Pourquoi Math.pow () n'est (parfois) pas égal à ** en JavaScript?

118

Je viens de découvrir la fonctionnalité ECMAScript 7 a**bcomme alternative à Math.pow(a,b)( MDN Reference ) et suis tombé sur une discussion dans ce post , dans laquelle ils se comportent apparemment différemment. Je l'ai testé dans Chrome 55 et je peux confirmer que les résultats diffèrent.

Math.pow(99,99) Retour 3.697296376497263e+197

tandis que

99**99 Retour 3.697296376497268e+197

Donc, consigner la différence se Math.pow(99,99) - 99**99traduit par -5.311379928167671e+182.

Jusqu'à présent, on pourrait dire que c'est simplement une autre implémentation, mais l'envelopper dans une fonction se comporte à nouveau différemment:

function diff(x) {
  return Math.pow(x,x) - x**x;
}

l'appel diff(99)revient 0.

Pourquoi cela se produit-il?

Comme l'a souligné xszaboj , cela peut être réduit à ce problème:

var x = 99;
x**x - 99**99; // Returns -5.311379928167671e+182
Thomas Altmann
la source
7
On dirait que quelqu'un a réécrit l'algorithme utilisé et qu'une erreur en virgule flottante a été trouvée. Les chiffres sont difficiles ...
krillgar
4
@krillgar semble raisonnable, mais pourquoi cette même erreur ne se produit-elle pas alors dans une fonction?
Thomas Altmann
3
@AndersonPimentel Le lien MDN pointe vers une table de compatibilité .
Álvaro González
7
la différence est entre ces deux: var x = 99; x * * x; et 99 * * 99. Ou fonction diff (x) {return 99 * * 99 - (x * * x); }; diff (99). Désolé pour l'espacement, les commentaires filtres deux étoiles :(
xszaboj
1
@xszaboj met du code dans des backticks `likethis`pour le rendre lisible et éviter le problème gras / italique
phuclv

Réponses:

126

99**99est évalué au moment de la compilation ("repli constant"), et la powroutine du compilateur est différente de celle du runtime . Lors de l'évaluation **au moment de l'exécution, les résultats sont identiques à Math.pow- pas étonnant car il **est en fait compilé en un Math.powappel:

console.log(99**99);           // 3.697296376497268e+197
a = 99, b = 99;
console.log(a**b);             // 3.697296376497263e+197
console.log(Math.pow(99, 99)); // 3.697296376497263e+197

Réellement

99 99 = 369729637649726772657187905628805440595668764281741102430259972423552570455277523421410650010128232727940978889548326540119429996769494359451621570193644014419989982007106061570193644014419989989989969971060

le premier résultat est donc une meilleure approximation, mais un tel écart entre les expressions constantes et dynamiques ne devrait pas avoir lieu.

Ce comportement ressemble à un bogue dans la V8. Il a été signalé et devrait être corrigé bientôt.

Georg
la source
19
Donc, c'est essentiellement JS qui essaie d'améliorer les performances avec le calcul 99**99au préalable? Cela pourrait-il être considéré comme un bogue, car Math.powcrée la même sortie pour les nombres et les variables et **ne le fait pas?
Thomas Altmann
3
@ThomasAltmann: Math.rowest toujours à l'exécution, le pliage const ne peut être effectué que pour les opérateurs. Oui, c'est définitivement un bug.
georg
11
Un bug a été enregistré , par l'apparence des choses par l'OP ici.
James Thorpe
5
J'utilise MS Edge, et les 3 résultats sont les mêmes: 3.697296376497263e+197, 3.697296376497263e+197et 3.697296376497263e+197respectivement. C'est très certainement un bogue de Chrome.
Nolonar
4
@ThomasAltmann si pliage constant produit une pire valeur que le impl d'exécution alors il est un bug. S'il produit une meilleure valeur que le runtime, il peut être considéré ou non comme un bogue. Dans ce cas, c'est mieux - la valeur correcte est "... 26772 ...", le pliage constant produit "... 268" (correctement arrondi) et le runtime produit "... 263" (désactivé de 4+ unités en dernier lieu).
hobbs