122

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
Community
  • 1
  • 1
Thomas Altmann
  • 1,744
  • 2
  • 11
  • 16
  • 7
    It sounds like someone rewrote the algorithm they used, and a [floating point error](http://stackoverflow.com/q/1458633/1195056) was found. Numbers are hard... – krillgar Jan 16 '17 at 15:01
  • 4
    @krillgar sounds reasonable, but why isn't that same error happening in a function then? – Thomas Altmann Jan 16 '17 at 15:02
  • It's not necessarily a function. I just tried with the values hard coded inside a function, and get the same difference (Chrome 55.0.2883.87 m console). However, using a variable does return 0. Perhaps it's something with being passed as a variable results in another imprecise floating point error? I'm grasping at straws though with that. – krillgar Jan 16 '17 at 15:06
  • If that matters, my [calculator](http://speedcrunch.org/) produces the same output than `Math.pow()`. – Álvaro González Jan 16 '17 at 15:07
  • Firefox 45.1 does not recognize the `**` operator. – Anderson Pimentel Jan 16 '17 at 15:08
  • 3
    @AndersonPimentel The MDN link points to a [compatibility table](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Browser_compatibility). – Álvaro González Jan 16 '17 at 15:08
  • @ÁlvaroGonzález Thanks! Missed that. – Anderson Pimentel Jan 16 '17 at 15:09
  • That's an interesting catch `**` is giving more correct answer. According to standard JS guarantees precision up to 2 ^ 53 − 2. So if number is no more taht this - we could expect any behavior. But still I believe that different results is bug in v8 – Andrey Jan 16 '17 at 15:10
  • @Andrey I could get that the implementation just differs due to the nature of working with pretty huge numbers but what I can't wrap my head around is, why that error does not occur when the value is passed in a function as variable. Either the error in `**` doesn't occur or the same error suddenly comes up in `Math.pow` – Thomas Altmann Jan 16 '17 at 15:14
  • 7
    difference is between this two: var x = 99; x * * x ; and 99 * * 99. Or function diff(x) { return 99 * * 99 - (x * * x); }; diff(99). Sorry for spacing, Comment filters two stars :( – xszaboj Jan 16 '17 at 15:16
  • @xszaboj Wrap your code in backticks to fix the formatting. `\`like this\`` – melpomene Jan 16 '17 at 15:27
  • @JamesThorpe Test: `function diff(x) { return 99 ** 99 - (x ** x); }; diff(99)` – melpomene Jan 16 '17 at 15:32
  • 1
    @xszaboj put code into backticks [`\`likethis\``](http://stackoverflow.com/help/formatting) to make it readable and also avoid the bold/italic problem – phuclv Jan 17 '17 at 02:39
  • 1
    Why do they even add `**` as an alternative for `Math.pow`? Yet another unnecessary syntax that most people will not know and cause unreadable code – Alexander Derck Feb 08 '17 at 08:16

1 Answers1

128

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.

georg
  • 211,518
  • 52
  • 313
  • 390
  • 19
    So it's basically JS trying to improve performance with computing `99**99` beforehand? Could this be considered a bug, since `Math.pow` creates the same output for numbers and variables and `**` doesn't? – Thomas Altmann Jan 16 '17 at 15:26
  • 3
    @ThomasAltmann: `Math.row` is always runtime, const folding can only be done for operators. Yes, it's definitely a bug. – georg Jan 16 '17 at 15:37
  • 11
    A bug [has been logged](https://bugs.chromium.org/p/v8/issues/detail?id=5848), by the looks of things by the OP here. – James Thorpe Jan 16 '17 at 16:28
  • 6
    I'm using MS Edge, and all 3 results are the same: `3.697296376497263e+197`, `3.697296376497263e+197`, and `3.697296376497263e+197` respectively. It's most definitely a Chrome bug. – Nolonar Jan 16 '17 at 20:33
  • 4
    @ThomasAltmann if constant folding produces a *worse* value than the runtime impl then it's a bug. If it produces a *better* value than the runtime then it might or might not be considered a bug. In this case, it's better — the correct value is "... 26772...", constant folding produces "...268" (correctly rounded), and the runtime produces "...263" (off by 4+ units in the last place). – hobbs Jan 17 '17 at 08:32
  • 1
    @hobbs If it's producing a different result than the runtime, then it's most definitely a compiler optimisation bug, better value or not. Whether the runtime itself could be improved to a better value (and therefore also the compiler optimisation) is a separate issue. – James Thorpe Jan 17 '17 at 17:25
  • Your snippet is broken, it seems. `"message": "SyntaxError: expected expression, got '*'",` (I can't into JavaScript, sorry, no idea what's wrong) – R. Martinho Fernandes Mar 10 '17 at 13:25
  • 1
    @R.MartinhoFernandes: `**` only works in recent Chrome/FF (52+) – georg Mar 10 '17 at 14:00