Por que Math.pow () (às vezes) não é igual a ** em JavaScript?

118

Acabei de descobrir o recurso ECMAScript 7 a**bcomo uma alternativa para Math.pow(a,b)( Referência MDN ) e me deparei com uma discussão nesse post , na qual eles aparentemente se comportam de maneira diferente. Eu testei no Chrome 55 e posso confirmar que os resultados são diferentes.

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

enquanto que

99**99 retorna 3.697296376497268e+197

Portanto, registrar a diferença Math.pow(99,99) - 99**99resulta em -5.311379928167671e+182.

Até agora, pode-se dizer que é simplesmente outra implementação, mas envolvê-la em uma função se comporta de forma diferente novamente:

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

diff(99)retornos de chamada 0.

Por que isso está acontecendo?

Como xszaboj apontou, isso pode ser reduzido a este problema:

var x = 99;
x**x - 99**99; // Returns -5.311379928167671e+182
Thomas Altmann
fonte
7
Parece que alguém reescreveu o algoritmo usado e um erro de ponto flutuante foi encontrado. Os números são difíceis ...
Krillgar
4
@krillgar parece razoável, mas por que o mesmo erro não está acontecendo em uma função então?
Thomas Altmann
3
@AndersonPimentel O link MDN aponta para uma tabela de compatibilidade .
Álvaro González
7
a diferença é entre estes dois: var x = 99; x * * x; e 99 * * 99. Ou função diff (x) {retornar 99 * * 99 - (x * * x); }; diff (99). Desculpe pelo espaçamento, o comentário filtra duas estrelas :(
xszaboj
1
@xszaboj colocou o código em crases `likethis`para torná-lo legível e também evitar o problema de negrito / itálico
phuclv

Respostas:

126

99**99é avaliada em tempo de compilação ("dobramento constante"), e a powrotina do compilador é diferente da de tempo de execução . Ao avaliar **em tempo de execução, os resultados são idênticos a Math.pow- não é de se admirar, já que **na verdade é compilado para uma Math.powchamada:

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

Na realidade

99 99 = 369729637649726772657187905628805440595668764281741102430259972423552570455277523421410650010128232727940978889548326540119429996769494359451621570193644014418071060667659301384999779999159200499899

então o primeiro resultado é uma aproximação melhor, ainda que tal discrepância entre expressões constantes e dinâmicas não deva ocorrer.

Este comportamento parece um bug no V8. Foi relatado e esperamos ser corrigido em breve.

georg
fonte
19
Então, é basicamente JS tentando melhorar o desempenho com a computação de 99**99antemão? Isso poderia ser considerado um bug, já que Math.powcria a mesma saída para números e variáveis ​​e **não?
Thomas Altmann
3
@ThomasAltmann: Math.rowé sempre tempo de execução, o dobramento const só pode ser feito para operadores. Sim, é definitivamente um bug.
georg
11
Um bug foi registrado , pelo que parece, pelo OP aqui.
James Thorpe
5
Eu estou usando o MS Edge, e todos os 3 resultados são os mesmos: 3.697296376497263e+197, 3.697296376497263e+197e, 3.697296376497263e+197respectivamente. É definitivamente um bug do Chrome.
Nolonar
4
@ThomasAltmann se o dobramento constante produzir um valor pior do que o tempo de execução impl, então é um bug. Se ele produzir um valor melhor do que o tempo de execução, pode ou não ser considerado um bug. Neste caso, é melhor - o valor correto é "... 26772 ...", a dobra constante produz "... 268" (arredondado corretamente), e o tempo de execução produz "... 263" (desligado em 4+ unidades em último lugar).
hobbs