Kayan nokta sayılarının göreceli karşılaştırması


10

f(x, y)Bazı formül uygulayan bir çift kayan nokta sayısı döndürerek sayısal bir işlevi var ve parametrelerin tüm birleşimi için analitik ifadelere karşı doğru olup olmadığını kontrol etmek istiyorum xve yilgileniyorum. Hesaplanan ve karşılaştırmak için doğru yolu nedir analitik kayan nokta sayıları?

Diyelim ki iki sayı ave b. Şimdiye kadar hem mutlak ( abs(a-b) < eps) hem de relative ( abs(a-b)/max(abs(a), abs(b)) < eps) hatalarının eps'den daha az olduğundan emin oldum . Bu şekilde, rakamlar 1e-20 civarında olsa bile sayısal yanlışlıkları yakalayacaktır.

Ancak, bugün bir sorun keşfettim, sayısal değer ave analitik değer b:

In [47]: a                                                                     
Out[47]: 5.9781943146790832e-322

In [48]: b                                                                     
Out[48]: 6.0276008792632078e-322

In [50]: abs(a-b)                                                              
Out[50]: 4.9406564584124654e-324

In [52]: abs(a-b) / max(a, b)                                                  
Out[52]: 0.0081967213114754103

Dolayısıyla mutlak hata [50] (açıkçası) küçüktür, ancak göreli hata [52] büyüktür. Bu yüzden programımda bir hatam olduğunu düşündüm. Hata ayıklayarak , bu sayıların denormal olduğunu fark ettim . Bu nedenle, uygun göreceli karşılaştırmayı yapmak için aşağıdaki rutini yazdım:

real(dp) elemental function rel_error(a, b) result(r)
real(dp), intent(in) :: a, b
real(dp) :: m, d
d = abs(a-b)
m = max(abs(a), abs(b))
if (d < tiny(1._dp)) then
    r = 0
else
    r = d / m
end if
end function

Nerede tiny(1._dp)bilgisayarımda 2.22507385850720138E-308 döndürür. Şimdi her şey çalışıyor ve göreceli hata olarak 0 alıyorum ve her şey yolunda. Özellikle, yukarıdaki göreceli hata [52] yanlıştır, basitçe denormal sayıların yetersiz doğruluğundan kaynaklanır. rel_errorFonksiyonu uygulamam doğru mu? Ben sadece abs(a-b)küçük (= denormal) daha küçük olup olmadığını kontrol ve 0 döndürmek gerekir? Yoksa diğer kombinasyonları kontrol etmeli miyim max(abs(a), abs(b))?

Sadece "uygun" yolun ne olduğunu bilmek istiyorum.

Yanıtlar:


11

Doğrudan kullanarak denormals kontrol edebilirsiniz isnormal()gelen math.h(daha sonra C99 ya da geç, POSIX.1'e veya). Fortran'da, modül ieee_arithmeticmevcutsa kullanabilirsiniz ieee_is_normal(). Bulanık eşitlik konusunda daha kesin olmak için, denormallerin kayan nokta gösterimini dikkate almalı ve sonuçların yeterince iyi olması için ne demek istediğinize karar vermelisiniz.

Daha da önemlisi, her iki sonucun da doğru olduğuna inanmak için, bir ara adımda çok fazla basamak kaybetmediğinizden emin olmalısınız. Denormallerle hesaplama genellikle güvenilir değildir ve algoritmanızın dahili olarak yeniden ölçeklendirilmesinden kaçınılmalıdır. Dahili ölçeklendirmenizin başarılı olduğundan emin olmak için feenableexcept(), C99 veya ieee_arithmeticFortran'daki modül ile kullanarak kayan nokta istisnalarını etkinleştirmenizi öneririz .

Uygulamanızın kayan nokta istisnalarında ortaya çıkan sinyali yakalamasını sağlayabilirsiniz, ancak denediğim tüm çekirdekler donanım bayrağını sıfırlar, böylece fetestexcept()yararlı bir sonuç döndürmez. Birlikte çalıştırıldığında -fp_trap, PETSc programları (varsayılan olarak) bir kayan nokta hatası yükseldiğinde yığın izlemesi yazdırır, ancak rahatsız eden satırı tanımlamaz. Bir hata ayıklayıcıda çalıştırırsanız, hata ayıklayıcı donanım bayrağını korur ve rahatsız edici ifadeyi keser. Kesin nedeni fetestexcept, sonucun bit olarak VEYA aşağıdaki bayrakların olduğu bir hata ayıklayıcıdan arayarak kesin nedeni kontrol edebilirsiniz (değerler makineye göre değişebilir, bkz fenv.h; bu değerler glibc ile x86-64 içindir).

  • FE_INVALID = 0x1
  • FE_DIVBYZERO = 0x4
  • FE_OVERFLOW = 0x8
  • FE_UNDERFLOW = 0x10
  • FE_INEXACT = 0x20

Mükemmel cevap için teşekkürler. Asimptotik rejimde karşılaştırdığım analitik ifade exp(log_gamma(m+0.5_dp) - (m+0.5_dp)*log(t)) / 2m = 234, t = 2000'dir. Ben arttıkça hızla sıfıra gider m. Tüm sayısal rutin benim en az 12 önemli basamağa (sıfır dönmek için de gayet iyi) "doğru" sayıları döndürmek emin olmak istiyorum. Hesaplama denormal bir sayı döndürürse, o zaman sadece sıfırdır ve sorun olmamalıdır. Dolayısıyla, sadece karşılaştırma rutininin buna karşı sağlam olması gerekir.
Ondřej Čertík

5

Donald Knuth'un "Bilgisayar Programlama Sanatı" cilt 2 "Seminer algoritmaları" nda kayan nokta karşılaştırma algoritması için bir önerisi vardır. C tarafından Th. Belding (bkz FCMP paketi ) ve kullanılabilir GSL .


2
İşte benim Fortran uygulaması: gist.github.com/3776847 , zaten içindeki denormal sayıları açıkça ele almam gerektiğini fark ettim. Aksi takdirde, göreceli hataya hemen hemen eşdeğer olduğunu düşünüyorum, tek fark, yapmak yerine, yaptığımız abs(a-b)/max(a, b) < eps, abs(a-b)/2**exponent(max(a, b)) < epsmantisin hemen hemen düşmesidir max(a, b), bu yüzden bence fark ihmal edilebilir.
Ondřej Čertík

5

Optimal olarak yuvarlatılmış denormalize sayılar gerçekten de yüksek göreceli hataya sahip olabilir. (Göreceli bir hatayı çağırırken bunu sıfırlamak yanıltıcıdır.)

Ancak sıfıra yakın, göreli hataların hesaplanması anlamsızdır.

Bu nedenle, normalleştirilmemiş sayılara ulaşmadan önce bile, muhtemelen mutlak doğruluğa (yani bu durumda garanti etmek istediğinize) geçmelisiniz.

Bu nedenle, aşağıdaki gibi bir formülün geçerliliğini kontrol ederek hesaplanan gerçek x'e karşı test etmenizi öneririmyx|yx|absacc+relaccmax(|x|,|y|)

Daha sonra kodunuzun kullanıcıları gerçekten ne kadar doğrulukta olduklarını tam olarak bilirler.


Sıfıra yakın göreceli hataları hesaplamanın anlamsız olduğundan emin misiniz? Sadece doğruluk kaybı varsa (ne sebeple olursa olsun) bunun anlamsız olduğunu düşünüyorum. Örneğin, bazı sayısal sorunlar nedeniyle (iki büyük sayının çıkarılması gibi) x <1e-150 için doğruluk kaybı varsa, haklısınız demektir. Ancak benim durumumda, sayılar denormal sayılara çarptığı zamanlar dışında sıfıra kadar doğru gibi görünüyor. Yani benim durumumda absacc = 1e-320 ya da öylesine ve sadece abs(a-b) < tiny(1._dp)yukarıda yaptığım gibi kontrol edebilirim .
Ondřej Čertík

@ OndřejČertík: Bu durumda 1e-150'yi 1e-300 ile değiştirin veya doğrulayabileceğiniz herhangi bir sınırlama yapın. Her durumda sıfıra çok yakın, mutlak bir hata yaparsınız ve hata talebiniz, göreli hatanın sıfır olduğunu bildirmek yerine bunu yansıtmalıdır.
Arnold Neumaier

Anlıyorum. Tümünün daha yüksek sayılar için çalıştığını doğrulayabilirim tiny(1._dp)=2.22507385850720138E-308(önceki yorumumda bir hata yaptım, 1e-320 değil, 2e-308). Bu benim mutlak hatam. O zaman göreceli hatayı karşılaştırmam gerekiyor. Ne demek istediğini anlıyorum, bence haklısın. Teşekkürler!
Ondřej Čertík

1
@ OndřejČertík: absacc verilen ek göreli hatayı bulmak için, maksimum . |yx|absaccmax(|x|,|y|)
Arnold Neumaier
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.