Saya memiliki fungsi numerik yang f(x, y)
mengembalikan angka floating point ganda yang mengimplementasikan beberapa rumus dan saya ingin memeriksa apakah itu benar terhadap ekspresi analitik untuk semua kombinasi parameter x
dan y
saya tertarik. Apa cara yang tepat untuk membandingkan yang dihitung dan dihitung angka floating point analitik?
Katakanlah dua angka itu adalah a
dan b
. Sejauh ini saya telah memastikan bahwa kedua kesalahan absolut ( abs(a-b) < eps
) dan juga relatif ( abs(a-b)/max(abs(a), abs(b)) < eps
) kurang dari eps. Dengan begitu ia akan menangkap ketidakakuratan numerik bahkan jika angkanya sekitar 1e-20.
Namun, hari ini saya menemukan masalah, nilai numerik a
dan nilai analitiknya b
adalah:
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
Jadi kesalahan absolut [50] adalah (jelas) kecil, tetapi kesalahan relatif [52] besar. Jadi saya pikir saya punya bug di program saya. Dengan men-debug, saya menyadari, bahwa angka-angka ini tidak normal . Karena itu, saya menulis rutin berikut untuk melakukan perbandingan relatif yang tepat:
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
Di mana tiny(1._dp)
mengembalikan 2.22507385850720138E-308 di komputer saya. Sekarang semuanya berfungsi dan saya hanya mendapatkan 0 sebagai kesalahan relatif dan semuanya baik-baik saja. Secara khusus, kesalahan relatif di atas [52] salah, itu hanya disebabkan oleh kurang akuratnya angka denormal. Apakah implementasi rel_error
fungsi saya benar? Haruskah saya memeriksa yang abs(a-b)
kurang dari kecil (= tidak normal), dan mengembalikan 0? Atau haruskah saya memeriksa beberapa kombinasi lain, seperti
max(abs(a), abs(b))
?
Saya hanya ingin tahu apa cara yang "tepat" itu.
sumber
exp(log_gamma(m+0.5_dp) - (m+0.5_dp)*log(t)) / 2
untuk m = 234, t = 2000. Ini menjadi nol dengan cepat saat saya bertambahm
. Semua saya ingin memastikan bahwa rutin numerik saya mengembalikan angka "benar" (untuk mengembalikan nol juga baik-baik saja) ke setidaknya 12 digit signifikan. Jadi, jika perhitungan mengembalikan angka yang tidak normal, maka itu hanya nol, dan seharusnya tidak ada masalah. Jadi, hanya perbandingan rutin harus kuat terhadap ini.Donald Knuth memiliki proposal untuk algoritma perbandingan titik mengambang dalam volume 2 "Algoritma seminumerik" dari "The Art of Computer Programming". Itu diimplementasikan dalam C oleh Th. Belding (lihat paket fcmp ) dan tersedia di GSL .
sumber
abs(a-b)/max(a, b) < eps
, kita lakukanabs(a-b)/2**exponent(max(a, b)) < eps
, yang cukup banyak hanya menjatuhkan mantissa dimax(a, b)
, jadi menurut saya perbedaannya dapat diabaikan.Angka denormalized yang dibulatkan secara optimal mungkin memang memiliki kesalahan relatif tinggi. (Menyiram ke nol sambil tetap menyebutnya kesalahan relatif menyesatkan.)
Tetapi mendekati nol, menghitung erros relatif tidak ada artinya.
Oleh karena itu, bahkan sebelum Anda mencapai angka denormalized, Anda mungkin harus beralih ke akurasi absolut (yaitu yang ingin Anda jamin dalam kasus ini).
Kemudian pengguna kode Anda tahu persis seberapa akurat yang mereka miliki.
sumber
abs(a-b) < tiny(1._dp)
seperti yang saya lakukan di atas.tiny(1._dp)=2.22507385850720138E-308
(saya membuat kesalahan dalam komentar saya sebelumnya, ini 2e-308, bukan 1e-320). Jadi ini adalah kesalahan mutlak saya. Maka saya perlu membandingkan kesalahan relatif. Saya mengerti maksud Anda, saya pikir Anda benar. Terima kasih!