Apakah loop 'untuk' ini berhenti, dan mengapa / mengapa tidak? untuk (var i = 0; 1 / i> 0; i ++) {}

104

Apakah forputaran ini pernah berhenti?

for (var i=0; 1/i > 0; i++) {
}

Jika ya, kapan dan mengapa? Saya diberitahu bahwa itu berhenti, tetapi saya tidak diberi alasan untuk itu.

Pembaruan

Sebagai bagian dari investigasi, saya telah menulis artikel yang cukup panjang dan mendetail yang menjelaskan semua yang terjadi di balik terpal - Inilah yang perlu Anda ketahui tentang jenis Nomor JavaScript

Max Koretskyi
sumber
5
Itu tidak akan berhenti. coba jalankan bagian kode ini. untuk (var i = 0; 1 / i> 0; i ++) {console.log (i)}
Sourabh Agrawal
3
Number.MAX_VALUE + 9.979202e291 == "Infinity" dan 1 / (NaN atau 'Infinity' atau 'undefined')> 0 == false.
askeet
6
Akankah Javascript mengabaikan loop itu karena tidak memiliki pernyataan di dalamnya? yaitu Optimalkan? Saya tahu ada beberapa bahasa yang bisa dikompilasi.
Brian J
3
@askeet, seperti yang ditunjukkan oleh gotnull dan yang lainnya di bawah, kami tidak pernah mencapai Infinity dengan meningkatkan berulang kali, alih-alih terjebak dalam lingkaran setelahnya Number.MAX_SAFE_INTEGER + 1.
LSpice

Jawaban:

128

(Saya bukan penggemar meta-konten, tapi: jawaban gotnull dan le_m sama-sama benar dan berguna. Awalnya, dan terlebih lagi dengan pengeditan yang dilakukan setelah Wiki Komunitas ini diposting. Motivasi asli untuk CW ini sebagian besar hilang sebagai akibat dari pengeditan tersebut, tetapi tetap berguna, jadi ... Juga: Meskipun hanya ada beberapa penulis yang terdaftar, banyak anggota komunitas lainnya telah sangat membantu dengan komentar yang telah dimasukkan dan dibersihkan. bukan hanya CW dalam nama.)


Perulangan tidak akan berhenti di mesin JavaScript yang diterapkan dengan benar. (Lingkungan host mesin pada akhirnya dapat menghentikannya karena tidak ada habisnya, tetapi itu adalah hal lain.)

Inilah alasannya:

  1. Awalnya, when iis 0, kondisinya 1/i > 0benar karena dalam JavaScript, 1/0is Infinity, dan Infinity > 0true.

  2. Setelah itu, iakan bertambah dan terus bertambah sebagai nilai integer positif untuk waktu yang lama (iterasi selanjutnya 9.007.199.254.740.991). Dalam semua kasus tersebut, 1/iakan tetap > 0(meskipun nilai untuk 1/imenjadi sangat kecil menjelang akhir!) Dan loop berlanjut ke dan termasuk loop di mana imencapai nilai Number.MAX_SAFE_INTEGER.

  3. Angka dalam JavaScript adalah titik mengambang biner presisi ganda IEEE-754, format yang cukup ringkas (64 bit) yang menyediakan penghitungan cepat dan jangkauan yang luas. Ini dilakukan dengan menyimpan angka sebagai bit tanda, eksponen 11-bit, dan signifikansi 52-bit (meskipun melalui kepintaran itu sebenarnya mendapat presisi 53 bit). Ini adalah titik mengambang biner (basis 2): Signifikan (ditambah beberapa kepintaran) memberi kita nilai, dan eksponen memberi kita besarnya angka tersebut.

    Secara alami, dengan begitu banyak bit yang signifikan, tidak setiap nomor dapat disimpan. Berikut adalah angka 1, dan angka tertinggi berikutnya setelah 1 yang dapat disimpan oleh format, 1 + 2 -52 ≈ 1,00000000000000022, dan tertinggi berikutnya setelah itu 1 + 2 × 2 -52 ≈ 1,00000000000000044:

       + ------------------------------------------------- -------------- tanda sedikit
      / + ------- + ---------------------------------------- -------------- eksponen
     / / | + ------------------------------------------------- + - signifikan
    / / | / |
    0 01111111111 000000000000000000000000000000000000000000000000000000
                    = 1
    0 01111111111 0000000000000000000000000000000000000000000000000001
                    ≈ 1,00000000000000022
    0 01111111111 0000000000000000000000000000000000000000000000000010
                    ≈ 1,00000000000000044
    

    Perhatikan lompatan dari 1.00000000000000022 ke 1.00000000000000044; tidak ada cara untuk menyimpan 1.0000000000000003. Itu juga bisa terjadi dengan bilangan bulat: Number.MAX_SAFE_INTEGER(9,007,199,254,740,991) adalah nilai bilangan bulat positif tertinggi yang dapat disimpan oleh format di mana idan i + 1keduanya dapat direpresentasikan secara tepat ( spesifikasi ). Baik 9.007.199.254.740.991 dan 9.007.199.254.740.992 dapat direpresentasikan, tetapi bilangan bulat berikutnya , 9.007.199.254.740.993, tidak dapat; bilangan bulat berikutnya yang dapat kita wakili setelah 9.007.199.254.740.992 adalah 9.007.199.254.740.994. Berikut adalah pola bitnya, perhatikan bit paling kanan (paling tidak signifikan):

       + ------------------------------------------------- -------------- tanda sedikit
      / + ------- + ---------------------------------------- -------------- eksponen
     / / | + ------------------------------------------------- + - signifikan
    / / | / |
    0 10000110011 1111111111111111111111111111111111111111111111111111
                    = 9007199254740991 (Nomor.MAX_SAFE_INTEGER)
    0 10000110100 000000000000000000000000000000000000000000000000000000
                    = 9007199254740992 (Bilangan.MAX_SAFE_INTEGER + 1)
    x xxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
                      9007199254740993 (Number.MAX_SAFE_INTEGER + 2) tidak dapat disimpan
    0 10000110100 0000000000000000000000000000000000000000000000000001
                    = 9007199254740994 (Bilangan.MAX_SAFE_INTEGER + 3)
    

    Ingat, formatnya adalah basis 2, dan dengan eksponen tersebut bit yang paling tidak signifikan tidak lagi pecahan; itu memiliki nilai 2. Bisa off (9,007,199,254,740,992) atau pada (9,007,199,254,740,994); jadi pada titik ini, kita mulai kehilangan presisi bahkan pada skala bilangan bulat (integer). Yang berimplikasi pada loop kami!

  4. Setelah menyelesaikan i = 9,007,199,254,740,992perulangan, i++berikan kami ... i = 9,007,199,254,740,992lagi; tidak ada perubahan i, karena bilangan bulat berikutnya tidak dapat disimpan dan kalkulasi berakhir dengan pembulatan ke bawah. iakan berubah jika kita melakukannya i += 2, tetapi i++tidak dapat mengubahnya. Jadi kita telah mencapai kondisi mapan: itidak pernah berubah, dan loop tidak pernah berhenti.

Berikut berbagai perhitungan yang relevan:

if (!Number.MAX_SAFE_INTEGER) {
  // Browser doesn't have the Number.MAX_SAFE_INTEGER
  // property; shim it. Should use Object.defineProperty
  // but hey, maybe it's so old it doesn't have that either
  Number.MAX_SAFE_INTEGER = 9007199254740991;
}
var i = 0;
console.log(i, 1/i, 1/i > 0); // 0, Infinity, true
i++;
console.log(i, 1/i, 1/i > 0); // 1, 1, true
// ...eventually i is incremented all the way to Number.MAX_SAFE_INTEGER
i = Number.MAX_SAFE_INTEGER;
console.log(i, 1/i, 1/i > 0); // 9007199254740991 1.1102230246251568e-16, true
i++;
console.log(i, 1/i, 1/i > 0); // 9007199254740992 1.1102230246251565e-16, true
i++;
console.log(i, 1/i, 1/i > 0); // 9007199254740992 1.1102230246251565e-16, true (no change)
console.log(i == i + 1);      // true

TJ Crowder
sumber
79

Menjawab:

Ketentuan 1/i > 0akan selalu bernilai benar:

  • Awalnya ini benar karena 1/0mengevaluasi ke Infinitydan Infinity > 0benar

  • Itu tetap benar karena 1/i > 0benar untuk semua i < Infinitydan i++tidak pernah mencapai Infinity.

Mengapa tidak i++pernah mencapai Infinity? Karena ketepatan Numbertipe data yang terbatas , ada nilai yang i + 1 == i:

9007199254740992 + 1 == 9007199254740992 // true

Setelah imencapai nilai itu (yang sesuai dengan ), nilainya akan tetap sama bahkan setelahnya .Number.MAX_SAFE_INTEGER + 1i++

Oleh karena itu kami memiliki loop tak terbatas.


Lampiran:

Mengapa 9007199254740992 + 1 == 9007199254740992?

Jenis Numberdata JavaScript sebenarnya adalah pelampung presisi ganda IEEE 754 64-bit . Masing Number- masing dibongkar dan disimpan sebagai tiga bagian: tanda 1-bit, eksponen 11-bit, dan mantissa 52-bit. Nilainya adalah -1 tanda × mantissa × 2 eksponen .

Bagaimana 9007199254740992 direpresentasikan? Sebagai 1.0 × 2 53 , atau dalam biner:

masukkan deskripsi gambar di sini

Dengan menambah sedikit mantissa yang paling tidak signifikan, kita mendapatkan angka berikutnya yang lebih tinggi:

masukkan deskripsi gambar di sini

Nilai angka itu adalah 1.00000000000000022… × 2 53 = 9007199254740994

Apa artinya? Numberdapat berupa 900719925474099 2 atau 900719925474099 4 , tetapi tidak ada di antaranya.

Sekarang, yang mana yang akan kita pilih untuk mewakili 900719925474099 2 + 1 ? The IEEE 754 pembulatan aturan memberikan jawaban: 900719925474099 2 .

le_m
sumber
9
pendek dan benar, lebih baik daripada jawaban yang diterima saat ini
AlexWien
@AlexWien Jawaban yang diterima adalah jawaban yang diterima wiki komunitas.
fulvio
2
Saya tidak tahu istilah jawaban "diterima wiki komunitas". Apa hubungannya ini dengan stackoverflow? Jika ini adalah tautan asing, tautan harus disediakan. Jawaban yang diterima di stackoverflow selalu dapat berubah, status yang diterima belum final.
AlexWien
"Mengapa i ++ tidak pernah mencapai Infinity? Karena ketepatan terbatas dari tipe data Number ..." <- Tentunya itu tidak akan pernah mencapai infinity, bahkan dengan tipe angka presisi tak terbatas .. Anda tahu, karena Anda tidak bisa menghitung infinity: P
Blorgbeard keluar
1
@Blorgbeard Anda dapat menghitung hingga Tak Terhingga dengan presisi ganda terbatas, Anda hanya perlu menambah angka yang jauh lebih besar dari 1, mis for (var i = 0; i < Infinity; i += 1E306);. Tapi saya mengerti dari mana Anda berasal;)
le_m
27

The Number.MAX_SAFE_INTEGERkonstan mewakili bilangan bulat aman maksimum dalam JavaScript. The MAX_SAFE_INTEGERkonstan memiliki nilai 9007199254740991. Alasan di balik angka itu adalah bahwa JavaScript menggunakan angka format floating-point presisi ganda seperti yang ditentukan dalam IEEE 754 dan hanya dapat mewakili angka dengan aman antara - (2 53 - 1) dan 2 53 - 1.

Aman dalam konteks ini mengacu pada kemampuan untuk merepresentasikan bilangan bulat dengan tepat dan membandingkannya dengan benar. Misalnya, Number.MAX_SAFE_INTEGER + 1 === Number.MAX_SAFE_INTEGER + 2akan mengevaluasi ke true, yang secara matematis salah. Lihat Number.isSafeInteger()untuk informasi lebih lanjut.

Karena MAX_SAFE_INTEGERmerupakan properti statis Number, Anda selalu menggunakannya sebagai Number.MAX_SAFE_INTEGER, bukan sebagai properti Numberobjek yang Anda buat.

MEMPERBARUI:

Seseorang dalam jawaban yang dihapus menyebutkan: itidak akan pernah mencapai tak terhingga. Setelah tercapai Number.MAX_SAFE_INTEGER, i++jangan menaikkan variabel lagi. Ini sebenarnya tidak benar.

@TJ Crowder komentar bahwa i = Number.MAX_SAFE_INTEGER; i++; i == Number.MAX_SAFE_INTEGER;adalah false. Tetapi iterasi berikutnya mencapai keadaan tidak berubah, jadi jawaban utama benar.

idalam contoh tidak pernah tercapai Infinity.

fulvio
sumber
2
Secara khusus, 9007199254740992 + 1adalah 9007199254740992.
Kobi
1
@Gerardo, saya membayangkan itu akan terjadi.
fulvio
1
@Gerardourtado for (var i=0; NaN > 0; i++) { console.log(i); }tidak akan menghasilkan apa-apa.
fulvio
2
@ GerardoFurtado: Dalam hal ini, loop akan berhenti. Badan perulangan tidak akan pernah dimasukkan sama sekali, karena test pertama ( 1/i > 0) akan menjadi salah, karena jika iadalah 0, 1/iadalah NaN, dan NaN > 0salah.
TJ Crowder
1
@TJCrowder Saya telah memperbarui jawaban saya. Terima kasih telah menunjukkannya!
fulvio