Untuk nilai apa i melakukan while (i == i + 1) {} berulang selamanya?


120

Saya mengikuti teka-teki ini dari kursus pemrograman tingkat lanjut di ujian universitas Inggris .

Pertimbangkan loop berikut, di mana i, sejauh ini, tidak dideklarasikan:

while (i == i + 1) {}

Temukan definisi dari i, yang mendahului pengulangan ini, sehingga pengulangan while berlanjut untuk selamanya.

Pertanyaan berikutnya, yang menanyakan pertanyaan yang sama untuk cuplikan kode ini:

while (i != i) {}

jelas bagi saya. Tentu saja dalam situasi lain ini NaNtetapi saya benar-benar terjebak pada yang sebelumnya. Apakah ini ada hubungannya dengan overflow? Apa yang akan menyebabkan pengulangan seperti itu untuk selamanya di Java?


3
Adakah kemungkinan untuk mengganti .equals()metode? Karena saya tidak dideklarasikan, kami dapat menggunakan kelas apa pun yang kami inginkan.
Geno Chen

7
@Raedwald mempelajari kode "tidak profesional" membuat Anda lebih "profesional", jadi ... Bagaimanapun, itu pertanyaan yang bagus
Andrew Tobilko

12
Fakta menyenangkan, di C # ini juga berfungsi untuk tipe numerik nullable yang nilainya null, karena null == nullbenar, dan null + 1adalah null.
Eric Lippert

4
@EricDuminil: Situasinya jauh lebih buruk dari yang Anda bayangkan. Dalam banyak bahasa, aritmatika floating point harus dilakukan setidaknya dalam 64 bit presisi yang ditentukan oleh double, yang berarti dapat dilakukan dengan presisi yang lebih tinggi sesuai keinginan kompiler, dan dalam praktiknya hal ini terjadi . Saya dapat menunjukkan kepada Anda selusin pertanyaan di situs ini dari programmer C # yang bertanya-tanya mengapa 0.2 + 0.1 == 0.3nilainya berubah tergantung pada pengaturan kompiler, fase bulan, dan sebagainya.
Eric Lippert

5
@EricDuminil: Kesalahan ini disalahkan pada Intel, yang memberi kami satu set chip yang melakukan aritmatika floating point presisi lebih tinggi dan lebih cepat jika angkanya dapat didaftarkan, yang berarti bahwa hasil komputasi floating point dapat mengubah nilainya bergantung tentang seberapa baik penjadwal register di pengoptimal bekerja hari ini. Pilihan Anda sebagai perancang bahasa kemudian berada di antara penghitungan berulang dan penghitungan yang cepat dan tepat , dan komunitas yang peduli dengan matematika floating point akan memilih yang terakhir.
Eric Lippert

Jawaban:


142

Pertama-tama, karena while (i == i + 1) {}loop tidak mengubah nilai i, membuat loop ini tak terbatas sama dengan memilih nilai iyang memuaskan i == i + 1.

Ada banyak nilai seperti itu:

Mari kita mulai dengan yang "eksotis":

double i = Double.POSITIVE_INFINITY;

atau

double i =  Double.NEGATIVE_INFINITY;

Alasan pemenuhan nilai i == i + 1tersebut dinyatakan dalam
JLS 15.18.2. Operator Aditif (+ dan -) untuk Jenis Numerik :

Jumlah dari tak hingga dan nilai hingga sama dengan operan tak hingga.

Ini tidak mengherankan, karena menambahkan nilai hingga ke nilai tak hingga akan menghasilkan nilai tak hingga.

Meskipun demikian, sebagian besar nilai iyang memuaskan i == i + 1hanyalah nilai besar double(atau float):

Sebagai contoh:

double i = Double.MAX_VALUE;

atau

double i = 1000000000000000000.0;

atau

float i = 1000000000000000000.0f;

Jenis doubledan floatmemiliki presisi yang terbatas, jadi jika Anda mengambil nilai doubleatau yang cukup besar float, menambahkannya 1akan menghasilkan nilai yang sama.


10
Atau (double)(1L<<53)- ataufloat i = (float)(1<<24)
dave_thompson_085

3
@Ruslan: Ahli matematika mana pun tidak akan setuju. Angka floating point tidak masuk akal. Mereka adalah non-asosiatif, non-refleksif (NaN! = NaN), dan bahkan tidak dapat diganti (-0 == 0, tetapi 1/0! = 1 / -0). Jadi sebagian besar mesin aljabar tidak bisa diterapkan.
Kevin

2
@Kevin sementara bilangan floating-point memang tidak bisa terlalu masuk akal secara umum, perilaku infinities (yang dijelaskan dalam kalimat itu) dirancang untuk masuk akal.
Ruslan

4
@Kevin Agar adil terhadap float, jika Anda berurusan dengan infinitas atau nilai tak terdefinisi, Anda juga tidak dapat mengasumsikan properti yang Anda daftarkan dalam aljabar.
Voo

2
@Kevin: IMHO, matematika floating-point bisa jauh lebih masuk akal jika mereka mengganti konsep "nol positif dan negatif" tanda positif, negatif, dan tidak bertanda "infinitesimals" bersama dengan satu "nol benar", dan membuat NaN sama dengan dirinya sendiri. Nol sejati dapat berperilaku sebagai identitas aditif dalam semua kasus, dan operasi sesuatu yang melibatkan pembagian oleh infinitesimals akan kehilangan biasnya terhadap asumsi infinitesimals positif.
supercat

64

Teka-teki ini dijelaskan secara rinci dalam buku "Java Puzzlers: Traps, Pitfalls, and Corner Cases" oleh Joshua Bloch dan Neal Gafter.

double i = Double.POSITIVE_INFINITY;
while (i == i + 1) {}

atau:

double i = 1.0e40;
while (i == i + 1) {}

keduanya akan menghasilkan pengulangan tanpa batas, karena menambahkan 1nilai titik-mengambang yang cukup besar tidak akan mengubah nilai, karena tidak "menjembatani celah" ke penggantinya 1 .

Catatan tentang teka-teki kedua (untuk pembaca selanjutnya):

double i = Double.NaN;
while (i != i) {}

juga menghasilkan loop tak hingga, karena NaN tidak sama dengan nilai floating-point apa pun, termasuk dirinya sendiri 2 .


1 - Java Puzzlers: Traps, Pitfalls, dan Corner Case (bab 4 - Loopy Puzzlers).

2 - JLS §15.21.1



0

Hanya sebuah ide: bagaimana dengan boolean?

bool i = TRUE;

Bukankah ini kasus dimana i + 1 == i?


tergantung bahasanya. Banyak bahasa secara otomatis memaksa boolean menjadi int jika digabungkan dengan int. Yang lain melakukan seperti yang Anda sarankan - memaksa int menjadi boolean.
Carl Witthoft

8
Pertanyaan ini adalah pertanyaan Java, dan saran Anda tidak lolos kompilasi di Java (yang tidak memiliki +operator yang menggunakan a booleandan an intsebagai operan).
Eran

@Eran: itulah keseluruhan gagasan operator yang kelebihan beban. Anda dapat membuat Java boolean berperilaku seperti C ++.
Dominique

4
Kecuali Java tidak mendukung kelebihan beban operator, jadi Anda tidak bisa.
CupawnTae
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.