¿Por qué Math.pow () (a veces) no es igual a ** en JavaScript?

118

Acabo de descubrir la función ECMAScript 7 a**bcomo una alternativa para Math.pow(a,b)( Referencia de MDN ) y encontré una discusión en esa publicación , en la que aparentemente se comportan de manera diferente. Lo probé en Chrome 55 y puedo confirmar que los resultados son diferentes.

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

mientras

99**99 devoluciones 3.697296376497268e+197

Entonces, registrar la diferencia Math.pow(99,99) - 99**99da como resultado -5.311379928167671e+182.

Hasta ahora se podría decir que es simplemente otra implementación, pero envolverla en una función se comporta diferente nuevamente:

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

diff(99)devoluciones de llamada 0.

¿Por qué está pasando eso?

Como señaló xszaboj , esto se puede reducir a este problema:

var x = 99;
x**x - 99**99; // Returns -5.311379928167671e+182
Thomas Altmann
fuente
7
Parece que alguien reescribió el algoritmo que usaron y se encontró un error de punto flotante . Los números son difíciles ...
krillgar
4
@krillgar suena razonable, pero ¿por qué entonces no ocurre el mismo error en una función?
Thomas Altmann
3
@AndersonPimentel El enlace MDN apunta a una tabla de compatibilidad .
Álvaro González
7
la diferencia es entre estos dos: var x = 99; x * * x; y 99 * * 99. O función diff (x) {return 99 * * 99 - (x * * x); }; diff (99). Perdón por el espacio, el comentario filtra dos estrellas :(
xszaboj
1
@xszaboj puso el código en comillas invertidas `likethis`para que sea legible y también evitar el problema de negrita / cursiva
phuclv

Respuestas:

126

99**99se evalúa en tiempo de compilación ("plegado constante"), y la powrutina del compilador es diferente de la del tiempo de ejecución . Al evaluar **en tiempo de ejecución, los resultados son idénticos a Math.pow, no es de extrañar ya **que en realidad se compila en una Math.powllamada:

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

Realmente

99 99 = 36972963764972677265718790562880544059566876428174110243025997242355257045527752342141065001012823272794097888954832654011942999676949435945162157019364401467980730998

por lo que el primer resultado es una mejor aproximación, aún así tal discrepancia entre expresiones constantes y dinámicas no debería ocurrir.

Este comportamiento parece un error en V8. Se ha informado y se espera que se solucione pronto.

georg
fuente
19
Entonces, ¿es básicamente JS tratando de mejorar el rendimiento con la informática de 99**99antemano? ¿Podría esto considerarse un error, ya que Math.powcrea la misma salida para números y variables y **no?
Thomas Altmann
3
@ThomasAltmann: Math.rowsiempre es tiempo de ejecución, el plegado constante solo se puede realizar para operadores. Sí, definitivamente es un error.
georg
11
Se ha registrado un error , según parece, el OP aquí.
James Thorpe
5
Estoy usando MS Edge, y los 3 resultados son los mismos: 3.697296376497263e+197, 3.697296376497263e+197, y 3.697296376497263e+197respectivamente. Definitivamente es un error de Chrome.
Nolonar
4
@ThomasAltmann si el plegado constante produce un valor peor que el impl en tiempo de ejecución, entonces es un error. Si produce un valor mejor que el tiempo de ejecución, entonces podría o no ser considerado un error. En este caso, es mejor: el valor correcto es "... 26772 ...", el plegado constante produce "... 268" (redondeado correctamente) y el tiempo de ejecución produce "... 263" (apagado en 4+ unidades en el último lugar).
hobbs