(İ == i + 1) {} sonsuza kadar döngü yaparken i'nin hangi değeri için?


120

Bu bilmeceyi İngiltere'deki bir üniversite sınavında ileri düzey bir programlama kursundan geçtim .

Şimdiye kadar bildirilmemiş olduğum şu döngüyü düşünün:

while (i == i + 1) {}

While döngüsü sonsuza kadar devam edecek şekildei , bu döngüden önce gelen tanımını bulun .

Bu kod pasajı için aynı soruyu soran sonraki soru:

while (i != i) {}

benim için açıktı. Elbette bu diğer durumda öyle NaNama gerçekten öncekine takılı kaldım. Bunun taşma ile ilgisi var mı? Java'da böyle bir döngünün sonsuza dek döngüye girmesine ne sebep olabilir?


3
.equals()Yöntemi geçersiz kılma olasılığı var mı? I beyan edilmediğinden, istediğimiz herhangi bir sınıfı kullanabiliriz.
Geno Chen

7
@Raedwald "amatörce olmayan" kod okumak sizi daha "profesyonel" yapar, bu yüzden ... Her neyse, bu iyi bir soru
Andrew Tobilko

12
Eğlenceli gerçek, C # 'da bu aynı zamanda değerleri olan null yapılabilir sayısal türler için de işe yarar nullçünkü null == nulltrue ve null + 1is null.
Eric Lippert

4
@EricDuminil: Durum sandığınızdan çok daha kötü. Birçok dilde, kayan nokta aritmetiği , bir double ile belirtilen en az 64 bitlik hassasiyette yapılmalıdır , bu da derleyicinin kaprisinde daha yüksek hassasiyette yapılabileceği anlamına gelir ve pratikte bu olur . 0.2 + 0.1 == 0.3Derleyici ayarlarına, ayın evresine vb. Bağlı olarak değerinin neden değiştiğini merak eden C # programcılarının bu sitede yer alan bir düzine sorusuna işaret edebilirim .
Eric Lippert

5
@EricDuminil: Bu karmaşanın suçu, bize sayılar kaydedilebilirse daha yüksek hassasiyetli ve daha hızlı kayan nokta aritmetiği yapan bir çip seti veren Intel'e düşüyor ; bu, bir kayan nokta hesaplamasının sonuçlarının değerlerini değiştirebileceği anlamına geliyor. optimize edicideki kayıt planlayıcısının bugün ne kadar iyi çalıştığı hakkında. Bir dil tasarımcısı olarak seçimleriniz tekrarlanabilir hesaplamalar ile hızlı, hassas hesaplamalar arasındadır ve kayan nokta matematiğini önemseyen topluluk ikincisini tercih edecektir.
Eric Lippert

Yanıtlar:


142

Öncelikle, while (i == i + 1) {}döngü değerini değiştirmediğinden, ibu döngüyü sonsuz yapmak i, tatmin edici bir değer seçmeye eşdeğerdir i == i + 1.

Bu tür birçok değer var:

"Egzotik" olanlarla başlayalım:

double i = Double.POSITIVE_INFINITY;

veya

double i =  Double.NEGATIVE_INFINITY;

Bu değerlerin tatmin edici olmasının nedeni JLS 15.18.2'dei == i + 1 belirtilmiştir
. Sayısal Türler için Katkı Operatörleri (+ ve -) :

Bir sonsuzun ve sonlu bir değerin toplamı, sonsuz işlenene eşittir.

Bu şaşırtıcı değildir, çünkü sonsuz bir değere sonlu bir değer eklemek sonsuz bir değerle sonuçlanmalıdır.

Bununla birlikte i, tatmin eden değerlerin çoğu i == i + 1büyük double(veya float) değerlerdir:

Örneğin:

double i = Double.MAX_VALUE;

veya

double i = 1000000000000000000.0;

veya

float i = 1000000000000000000.0f;

doubleVe floattürleri, büyük yeterince almak eğer öyleyse, sınırlı hassasiyet var doubleyafloat ekleyerek, değeri 1aynı değere neden olur buna.


10
Veya (double)(1L<<53)- veyafloat i = (float)(1<<24)
dave_thompson_085

3
@Ruslan: Herhangi bir matematikçi buna katılmaz. Kayan nokta sayıları çok az anlam ifade eder. İlişkisel değildirler, dönüşlü değildirler (NaN! = NaN) ve hatta ikame edilemezler (-0 == 0, ancak 1/0! = 1 / -0). Yani cebir mekanizmasının çoğu uygulanamaz.
Kevin

2
@Kevin, kayan noktalı sayılar aslında çok fazla anlam ifade etmese de, sonsuzlukların davranışı (bu cümlede anlatılan) mantıklı olacak şekilde tasarlanmıştır.
Ruslan

4
@Kevin Yüzenlere karşı adil olmak gerekirse, sonsuzluklar veya tanımlanmamış değerlerle uğraşırsanız, cebirde listelediğiniz özellikleri de üstlenemezsiniz.
Voo

2
@Kevin: IMHO, kayan nokta matematiği, "pozitif ve negatif sıfır" işareti pozitif, negatif ve işaretsiz "sonsuz küçükler" kavramlarını bir "gerçek sıfır" ile değiştirseler ve yapsalar çok daha anlamlı olabilirdi. NaN kendisine eşittir. Gerçek sıfır, her durumda ek bir kimlik olarak davranabilir ve sonsuz küçüklerle bölünmeyi içeren işlemler, sonsuz küçüklerin pozitif olduğunu varsaymaya yönelik önyargılarını yitirir.
supercat

64

Bu bulmacalar, Joshua Bloch ve Neal Gafter tarafından yazılan "Java Bulmacaları: Tuzaklar, Tuzaklar ve Köşe Vakaları" kitabında ayrıntılı olarak anlatılmıştır.

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

veya:

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

her ikisi de sonsuz bir döngü ile sonuçlanacaktır, çünkü 1yeterince büyük bir kayan nokta değerine eklemek , değeri değiştirmeyecektir, çünkü bu, halefi 1 ile "boşluğu doldurmaz" .

İkinci bulmaca hakkında bir not (gelecekteki okuyucular için):

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

NaN kendisi de dahil olmak üzere herhangi bir kayan noktalı değere eşit olmadığı için sonsuz bir döngü ile sonuçlanır 2 .


1 - Java Bulmacaları: Tuzaklar, Tuzaklar ve Köşe Vakaları (bölüm 4 - Döngü Bulmacaları).

2 - JLS §15.21.1



0

Sadece bir fikir: booleanlar ne olacak?

bool i = TRUE;

Bu nerede bir durum değil i + 1 == imi?


dile bağlıdır. Çoğu dil, bir int ile birleştirildiğinde otomatik olarak boole'leri tamsaya zorlar. Diğerleri sizin önerdiğiniz gibi yapar - int'i bir boolean'a zorlar.
Carl Witthoft

8
Bu soru bir Java sorusudur ve öneriniz Java'daki derlemeden geçmez ( +a booleanve bir intişlenenleri alan bir operatör yoktur ).
Eran

@Eran: Operatörün aşırı yüklenmesi fikri bu. Java boole'lerinin C ++ olanlar gibi davranmasını sağlayabilirsiniz.
Dominique

4
Java'nın operatör aşırı yüklemesini desteklememesi dışında, bu yüzden yapamazsınız.
CupawnTae
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.