Perché Math.pow () (a volte) non è uguale a ** in JavaScript?


118

Ho appena scoperto la funzionalità ECMAScript 7 a**bcome alternativa a Math.pow(a,b)( Riferimento MDN ) e mi sono imbattuto in una discussione in quel post , in cui apparentemente si comportano in modo diverso. L'ho testato in Chrome 55 e posso confermare che i risultati sono diversi.

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

mentre

99**99 ritorna 3.697296376497268e+197

Quindi registrando la differenza si Math.pow(99,99) - 99**99ottiene -5.311379928167671e+182.

Finora si potrebbe dire che è semplicemente un'altra implementazione, ma avvolgerlo in una funzione si comporta di nuovo in modo diverso:

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

la chiamata diff(99)restituisce 0.

Perché sta succedendo?

Come ha sottolineato xszaboj , questo può essere ridotto a questo problema:

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

7
Sembra che qualcuno abbia riscritto l'algoritmo utilizzato e sia stato trovato un errore in virgola mobile . I numeri sono difficili ...
krillgar

4
@krillgar sembra ragionevole, ma perché allora non si verifica lo stesso errore in una funzione?
Thomas Altmann

3
@AndersonPimentel Il collegamento MDN punta a una tabella di compatibilità .
Álvaro González

7
la differenza è tra queste due: var x = 99; x * * x; e 99 * * 99. Oppure funzione diff (x) {return 99 * * 99 - (x * * x); }; diff (99).
Ci

1
@xszaboj inserisce il codice nei backtick `likethis`per renderlo leggibile ed evitare anche il problema in grassetto / corsivo
phuclv

Risposte:


126

99**99viene valutato in fase di compilazione ("ripiegamento costante") e la powroutine del compilatore è diversa da quella di runtime . Durante la valutazione **in fase di esecuzione, i risultati sono identici a Math.pow- non c'è da stupirsi poiché **è effettivamente compilato per una Math.powchiamata:

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

In realtà

99 99 = 3697296376497267726571879056288054405956687642817411024302599724235525704552775234214106500101282327279409788895483265401194299967694943594592162157019364994014997771060664

quindi il primo risultato è una migliore approssimazione, tuttavia una tale discrepanza tra espressioni costanti e dinamiche non dovrebbe verificarsi.

Questo comportamento sembra un bug in V8. È stato segnalato e, si spera, verrà risolto presto.


19
Quindi fondamentalmente JS sta cercando di migliorare le prestazioni con il calcolo in 99**99anticipo? Potrebbe essere considerato un bug, poiché Math.powcrea lo stesso output per numeri e variabili e **non lo fa?
Thomas Altmann

3
@ThomasAltmann: Math.rowè sempre runtime, il piegamento const può essere eseguito solo per gli operatori. Sì, è sicuramente un bug.
georg

11
È stato registrato un bug , dall'aspetto delle cose dall'OP qui.
James Thorpe,

5
Sto utilizzando MS bordo, e tutti e 3 i risultati sono gli stessi: 3.697296376497263e+197, 3.697296376497263e+197, e 3.697296376497263e+197, rispettivamente. È sicuramente un bug di Chrome.
Nolonar

4
@ThomasAltmann se il ripiegamento costante produce un valore peggiore rispetto al runtime impl, allora è un bug. Se produce un valore migliore rispetto al runtime, potrebbe o non potrebbe essere considerato un bug. In questo caso, è meglio: il valore corretto è "... 26772 ...", la piegatura costante produce "... 268" (arrotondato correttamente) e il tempo di esecuzione produce "... 263" (con 4+ unità all'ultimo posto).
hobbs
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.