Search code examples
javascriptgoogle-chromev8ecmascript-2016

Why is Math.pow() (sometimes) not equal to ** in JavaScript?


I've just discovered the ECMAScript 7 feature a**b as an alternative for Math.pow(a,b) (MDN Reference) and came across a discussion in that post, in which they apparently behave differently. I've tested it in Chrome 55 and can confirm that the results differ.

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

whereas

99**99 returns 3.697296376497268e+197

So logging the difference Math.pow(99,99) - 99**99 results in -5.311379928167671e+182.

So far it could be said, that it's simply another implementation, but wrapping it in a function behaves different again:

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

calling diff(99) returns 0.

Why is that happening?

As xszaboj pointed out, this can be narrowed down to this problem:

var x = 99;
x**x - 99**99; // Returns -5.311379928167671e+182

Solution

  • 99**99 is evaluated at compile time ("constant folding"), and the compiler's pow routine is different from the runtime one. When evaluating ** at run time, results are identical with Math.pow — no wonder since ** is actually compiled to a Math.pow call:

    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

    Actually

    9999=369729637649726772657187905628805440595668764281741102430259972423552570455277523421410650010128232727940978889548326540119429996769494359451621570193644014418071060667659301384999779999159200499899

    so the first result is a better approximation, still such a discrepancy between constant- and dynamic expressions shouldn't take place.

    This behavior looks like a bug in V8. It has been reported and will hopefully get fixed soon.