Warum ist Math.pow () (manchmal) in JavaScript nicht gleich **?

118

Ich habe gerade die ECMAScript 7-Funktion a**bals Alternative für Math.pow(a,b)( MDN-Referenz ) entdeckt und bin in diesem Beitrag auf eine Diskussion gestoßen , in der sie sich anscheinend anders verhalten. Ich habe es in Chrome 55 getestet und kann bestätigen, dass die Ergebnisse unterschiedlich sind.

Math.pow(99,99) kehrt zurück 3.697296376497263e+197

wohingegen

99**99 kehrt zurück 3.697296376497268e+197

Protokollieren Sie also den Unterschied Math.pow(99,99) - 99**99 führt also zu -5.311379928167671e+182.

Bisher könnte man sagen, dass es einfach eine andere Implementierung ist, aber das Einschließen in eine Funktion verhält sich wieder anders:

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

Berufung diff(99) zurückkehrt 0.

Warum passiert das?

Wie xszaboj betonte, kann dies auf dieses Problem eingegrenzt werden:

var x = 99;
x**x - 99**99; // Returns -5.311379928167671e+182
Thomas Altmann
quelle
7
Es hört sich so an, als hätte jemand den verwendeten Algorithmus neu geschrieben und ein Gleitkommafehler gefunden. Zahlen sind schwer ...
Krillgar
4
@krillgar klingt vernünftig, aber warum tritt dann nicht der gleiche Fehler in einer Funktion auf?
Thomas Altmann
3
@AndersonPimentel Der MDN-Link verweist auf eine Kompatibilitätstabelle .
Álvaro González
7
Der Unterschied liegt zwischen diesen beiden: var x = 99; x * * x; und 99 * * 99. Oder Funktion diff (x) {return 99 * * 99 - (x * * x); }; diff (99). Entschuldigung für den Abstand, Kommentar filtert zwei Sterne :(
xszaboj
1
@xszaboj Code in Backticks setzen `likethis`, um es lesbar zu machen und auch das Fett / Kursiv-Problem zu vermeiden
phuclv

Antworten:

126

99**99wird zur Kompilierungszeit ausgewertet ("konstantes Falten"), und die powRoutine des Compilers unterscheidet sich von der Routine zur Laufzeit . Bei der Auswertung **zur Laufzeit sind die Ergebnisse identisch mit Math.pow- kein Wunder, da **tatsächlich zu einem Aufruf kompiliert wirdMath.pow :

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

Tatsächlich

99 99 = 36972963764972677265718790562880544059566876428174110243025997242355257045527752342141065001012823272794097888954832654011942999676949435945162157019364401441999999

Das erste Ergebnis ist also eine bessere Annäherung, dennoch sollte eine solche Diskrepanz zwischen konstanten und dynamischen Ausdrücken nicht auftreten.

Dieses Verhalten sieht aus wie ein Fehler in V8. Es wurde berichtet und wird hoffentlich bald behoben.

georg
quelle
19
Es ist also im Grunde genommen JS, der versucht, die Leistung mit Computer im 99**99Voraus zu verbessern ? Könnte dies als Fehler angesehen werden, da Math.powfür Zahlen und Variablen dieselbe Ausgabe erstellt wird und **dies nicht der Fall ist?
Thomas Altmann
3
@ThomasAltmann: Math.rowist immer Laufzeit, const Folding kann nur für Operatoren durchgeführt werden. Ja, es ist definitiv ein Fehler.
Georg
11
Es wurde ein Fehler protokolliert , wie es vom OP hier aussieht.
James Thorpe
5
Ich verwende den Rand, und alle drei Ergebnisse sind die gleichen: 3.697296376497263e+197, 3.697296376497263e+197, und 3.697296376497263e+197jeweils. Es ist definitiv ein Chrome-Fehler.
Nolonar
4
@ThomasAltmann Wenn konstantes Falten einen schlechteren Wert als das Laufzeitimplement ergibt, ist es ein Fehler. Wenn es einen besseren Wert als die Laufzeit erzeugt, kann es als Fehler angesehen werden oder auch nicht. In diesem Fall ist es besser - der korrekte Wert ist "... 26772 ...", konstantes Falten ergibt "... 268" (richtig gerundet) und die Laufzeit ergibt "... 263" (um 4+ versetzt) Einheiten an letzter Stelle).
Hobbs