2017-01-12 50 views
12

Diğer bazı kodları/anlayışlarımı test etmek için sonsuz döngüler yapmakla uğraştım ve bu garip davranışa rastladım. 0'dan 2^24 sayma Aşağıdaki program içinde benim makinede < 100ms alır, ancak 2^25 sayma (, hala yürütme oluyor yazma anda) büyüklüğü daha zamanın emir alır.Neden 2^24'e kadar saymak hızlı bir şekilde çalışıyor, ancak 2^25'e kadar saymak daha uzun sürüyor?

Bu neden böyle?

Bu,

Windows'un 10.

64-bit kopya üzerinde, Java 1.8.0_101 altında TestClass.java

public class TestClass { 
    public static void main(String[] args) { 
     addFloats((float) Math.pow(2.0, 24.0)); 
     addFloats((float) Math.pow(2.0, 25.0)); 
    } 

    private static void addFloats(float number) { 
     float f = 0.0f; 
     long startTime = System.currentTimeMillis(); 

     while(true) { 
      f += 1.0f; 
      if (f >= number) { 
       System.out.println(f); 
       System.out.println(number + " took " + (System.currentTimeMillis() - startTime) + " msecs"); 
       break; 
      } 
     } 
    } 
} 
+0

sadece addFloats ((şamandıra) Math.pow (2.0, 25.0)) çalıştıran çalıştı görüyorum mattering ;? –

+5

Çünkü bir an f + 1,0 == f. –

+0

@OlegEstekhin Ahh, bu olacak. Ben bir aptalım. Teşekkürler! –

cevap

16
float ler temsil edilebilir minimum hassasiyet var olmasıdır

oldu float 'değeri azaldıkça daha büyük olur. 2^24 ile 2^25 arasında bir yerde, bir tane eklemek, değeri sonraki en büyük temsil edilebilir sayıya değiştirmek için artık yeterli değildir. Bu noktada, f, döngü boyunca her seferinde aynı değeri korur, çünkü f += 1.0f artık onu değiştirmez.

bu şekilde döngü değiştirirseniz:

while(true) { 
    float newF = f + 1.0f; 
    if(newF == f) System.out.println(newF); 
    f += 1.0f; 
    if (f >= number) { 
     System.out.println(f); 
     System.out.println(number + " took " + (System.currentTimeMillis() - startTime) + " msecs"); 
     break; 
    } 
} 

Bunun olmaması görebilirsiniz. f 2^24'e ulaştığı anda artmaya devam ediyor gibi görünüyor. Eğer 2^25 ile çalıştırırsanız

Kodun çıktısı

"1.6777216E7" sonsuz sayıda olacaktır.

kişinin size bir sonraki Temsil değeri söyler Math.nextAfter function kullanarak bu değeri test edebilirsiniz. Bu kodu çalıştırmayı deneyin edin: başlatıldığında neden

float value = (float)Math.pow(2.0, 24.0); 
System.out.println(Math.nextAfter(value, Float.MAX_VALUE) - value); 

sonraki temsil edilebilir değer sonra 2^24 bunun neden böyle mükemmel bir ayrıntısı için 2^24 + 2.

olduğunu görebilirsiniz ve öyle nerede this answer

+5

Diğer bir deyişle, ikincisi sadece daha uzun sürmez - sonsuz bir döngü! – yshavit

+1

2^24 ile 2^25 arasında bir alanda bulunduğunuzda, buradaki "float" değerleri tam olarak tamsayılar olduğunda, f + = 1.0f; 'yi kullandığınızda, tam matematiksel sonuç tek sayı bir tamsayı olur. Bu, en yakın temsil edilen iki sayı arasında tam ortasında olduğu için, çoğu uygulama [iki katına bile] (https://en.wikipedia.org/wiki/Singly_and_doubly_even) birini (yani, dörde bölünebilen) seçecektir. bu durumda ise, '16777216.0f + 1.0f' 16777218.0f + 1.0f' olacaktır 'ise' 16777216.0f' (herhangi bir artış) olacaktır' 16777220.0f' (gerçek artış + 2 dir). –

İlgili konular