Mengapa Math.pow () (terkadang) tidak sama dengan ** di JavaScript?

118

Saya baru saja menemukan fitur ECMAScript 7 a**bsebagai alternatif untuk Math.pow(a,b)( Referensi MDN ) dan menemukan diskusi di posting itu , di mana mereka tampaknya berperilaku berbeda. Saya telah mengujinya di Chrome 55 dan dapat mengonfirmasi bahwa hasilnya berbeda.

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

sedangkan

99**99 kembali 3.697296376497268e+197

Jadi mencatat Math.pow(99,99) - 99**99hasil perbedaan -5.311379928167671e+182.

Sejauh ini dapat dikatakan, bahwa ini hanyalah implementasi lain, tetapi membungkusnya dalam sebuah fungsi berperilaku berbeda lagi:

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

menelepon diff(99)kembali 0.

Mengapa itu terjadi?

Seperti yang ditunjukkan xszaboj , ini dapat dipersempit menjadi masalah ini:

var x = 99;
x**x - 99**99; // Returns -5.311379928167671e+182
Thomas Altmann
sumber
7
Sepertinya seseorang menulis ulang algoritme yang mereka gunakan, dan ditemukan kesalahan floating point . Angka sulit ...
krillgar
4
@krillgar terdengar masuk akal, tapi mengapa kesalahan yang sama tidak terjadi di suatu fungsi?
Thomas Altmann
3
@AndersonPimentel Tautan MDN mengarah ke tabel kompatibilitas .
Álvaro González
7
perbedaan antara keduanya: var x = 99; x * * x; dan 99 * * 99. Atau fungsi diff (x) {return 99 * * 99 - (x * * x); }; diff (99). Maaf untuk spasi, Komentar memfilter dua bintang :(
xszaboj
1
@xszaboj memasukkan kode ke dalam backticks `likethis`agar dapat dibaca dan juga menghindari masalah huruf tebal / miring
phuclv

Jawaban:

126

99**99adalah dievaluasi pada waktu kompilasi ( "konstan lipat"), dan compiler powrutin berbeda dari satu runtime . Saat mengevaluasi **pada waktu proses, hasilnya identik dengan Math.pow- tidak heran karena **sebenarnya dikompilasi ke Math.powpanggilan:

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

Sebenarnya

99 99 Data

jadi hasil pertama adalah perkiraan yang lebih baik, tetap saja perbedaan antara ekspresi konstan dan dinamis tidak boleh terjadi.

Perilaku ini terlihat seperti bug di V8. Ini telah dilaporkan dan mudah-mudahan akan segera diperbaiki.

georg
sumber
19
Jadi pada dasarnya JS mencoba meningkatkan kinerja dengan komputasi 99**99sebelumnya? Mungkinkah ini dianggap bug, karena Math.powmembuat keluaran yang sama untuk angka dan variabel dan **tidak?
Thomas Altmann
3
@ThomasAltmann: Math.rowselalu runtime, pelipatan const hanya dapat dilakukan untuk operator. Ya, itu pasti bug.
georg
11
Sebuah bug telah dicatat oleh OP di sini.
James Thorpe
5
Saya menggunakan MS Edge, dan semua 3 hasilnya sama: 3.697296376497263e+197, 3.697296376497263e+197, dan 3.697296376497263e+197masing-masing. Ini pasti bug Chrome.
Nolonar
4
@ThomasAltmann jika pelipatan konstan menghasilkan nilai yang lebih buruk daripada runtime impl maka itu adalah bug. Jika menghasilkan nilai yang lebih baik daripada runtime maka itu mungkin atau mungkin tidak dianggap sebagai bug. Dalam kasus ini, lebih baik - nilai yang benar adalah "... 26772 ...", pelipatan konstan menghasilkan "... 268" (dibulatkan dengan benar), dan runtime menghasilkan "... 263" (berkurang 4+ unit di tempat terakhir).
hobbs