Floating Point Karşılaştırması: Neden Kesin Eşitlik Kontrolü Tehlikeli?
Floating point karşılaştırmasında tolerans aralığı yerine başka yöntem kullandın mı?
Bir floating point sayısını başka bir sayıyla tam eşitlik kontrolü yaparsanız, kod çalışırken beklenmeyen sonuçlarla karşılaşırsınız. Bunun nedeni bilgisayarların ondalıklı sayıları nasıl sakladığında yatıyor.
Bilgisayarlar floating point sayıları ikili (binary) formatta depoluyorlar. Bazı ondalıklı sayılar, örneğin 0.1, ikili sistemde tam olarak temsil edilemiyor. 0.1'i ikili olarak yazarsanız, sonsuz tekrarlayan bir kesir elde edersiniz. Bilgisayar bu sonsuz kesri kesmek zorunda kalıyor, yani yaklaşık bir değer saklıyor.
Bu sorunu görmek için bir örnek:
1float x = 0.1f + 0.2f;2if (x == 0.3f) {3cout << "Eşit" << endl;4} else {5cout << "Eşit değil" << endl; // Burası çalışır6}Çoğu dilde bu kod "Eşit değil" yazdırır. Matematiksel olarak 0.1 + 0.2 = 0.3 olsa da, floating point hatalı hesaplama nedeniyle x değeri 0.3'ten biraz farklı olur.
Doğru yol tolerans aralığı (epsilon) kullanmaktır:
1float epsilon = 1e-6f;2if (fabs(x - 0.3f) < epsilon) {3cout << "Eşit (tolerans içinde)" << endl;4}Bu yöntemi neden tercih etmelisin? Çünkü iki sayının arasındaki fark çok küçükse (epsilon'dan küçükse), pratik olarak eşit sayılabilirler. Epsilon değeri uygulamanın hassasiyet gereksinimlerine göre seçilir. Grafik programlama, fizik simülasyonu ya da finansal hesaplamalar gibi alanlarda bu kritik hale gelir.
Bir başka dikkat edilmesi gereken nokta: sabit bir epsilon değeri her zaman çalışmaz. Çok büyük sayıları karşılaştırırken epsilon'u uyarlamak gerekebilir. Örneğin 1000000.1 ile 1000000.2'yi karşılaştırırken 1e-6 epsilon yetersiz kalabilir. Daha güvenilir bir yöntem, iki sayının büyüklüğüne göre epsilon'u ayarlamaktır (relative epsilon).
bknz: Oyun Döngüsü: Delta Time ile Hızdan Bağımsız Hareket
floating point sayılarında == operatörü kullanmak tehlikeli, hatta yanlış sonuç verir. Tolerans aralığı ile karşılaştırma yapman gerekir. Bu basit bir kural gibi görünse de, bunu unutmuş kod sayısı milyonlarca. Oyun motorları, grafik kütüphaneleri ve fizik motorları bu hataya düşmemek için özel fonksiyonlar sağlıyor; sen de sağlamalısın.
5 yorum
ay bu gerçekten tuzak bir konu 0 1 + 0 2 = 0 3 çıkması çok garip geliyor başta ama sonra anlaşılıyor işte
bence bu sadece başta garip geliyor sonra aslında mantıklı hale geliyor
ama mantıklı olması da ayrı mesele ya ikili sistemde saklama filan tamam ama kullanıcı açısından 0 1 + 0 2 nin 0 3 etmemesi çok kafa karıştırıcı
ya bence o yüzden epsilon karşılaştırması var işte ona da alışman lazım sonuçta kod yazarken hep bu şekilde kontrol etmen gerek
ama epsilon de hep doğru sonuç vermiyor ya hangi değeri seçeceğini nereden bileyim