浮動小数点数の相対比較


10

f(x, y)いくつかの数式を実装する二重浮動小数点数を返す数値関数があり、パラメーターのすべての組み合わせの分析式に対して正しいことxy、興味があることを確認したいと思います。計算された値と分析浮動小数点数?

2つの数値がaとであるとしましょうb。これまでのところ、絶対abs(a-b) < epsabs(a-b)/max(abs(a), abs(b)) < eps)エラーと相対()エラーの両方がeps未満であることを確認しています。このようにすると、たとえ1e-20程度の数値であったとしても、数値の不正確さをキャッチできます。

しかし、今日、私は問題を発見しました。数値aと分析値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

したがって、絶対誤差[50]は(明らかに)小さいですが、相対誤差[52]は大きいです。そのため、プログラムにバグがあると思いました。デバッグにより、これらの数値は非正規であることがわかりました。そのため、適切な相対比較を行うために次のルーチンを作成しました。

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

どこにtiny(1._dp)自分のコンピュータ上の2.22507385850720138E-308を返します。これですべてが機能し、相対エラーとして0が表示され、すべて問題ありません。特に、上記の相対エラー[52]は間違っています。これは、単に非正規数の精度が不十分であるために発生します。rel_error関数の実装は正しいですか?それabs(a-b)がtiny(=非正規)未満であることを確認し、0を返すだけですか?それとも、他の組み合わせを確認する必要があり max(abs(a), abs(b))ますか?

「適切な」方法が何であるかを知りたいだけです。

回答:


11

isnormal()from math.h(C99以降、POSIX.1以降)を使用して、デノーマルを直接チェックできます。Fortranでは、モジュールieee_arithmeticが使用可能な場合は、を使用できますieee_is_normal()。ファジー等式についてより正確に説明するには、デノーマルの浮動小数点表現を検討し、結果が十分に良いとはどういう意味かを決定する必要があります。

さらに言えば、どちらの結果も正しいと信じるためには、中間のステップで桁落ちが多すぎないことを確認する必要があります。デノーマルでの計算は一般に信頼性が低く、アルゴリズムを内部で再スケーリングさせることで回避する必要があります。内部スケーリングを確実に成功させるためにfeenableexcept()、C99またはieee_arithmeticFortran のモジュールを使用して、浮動小数点例外をアクティブにすることをお勧めします。

浮動小数点例外で発生するシグナルをアプリケーションでキャッチすることはできますが、私が試したすべてのカーネルはハードウェアフラグをリセットしたためfetestexcept()、有用な結果が返されません。で実行すると-fp_trap、PETScプログラムは(デフォルトで)浮動小数点エラーが発生したときにスタックトレースを出力しますが、問題の行は識別しません。デバッガーで実行する場合、デバッガーはハードウェアフラグを保持し、問題のある式で中断します。fetestexceptデバッガーから呼び出して、結果が次のフラグのビット単位のORである正確な理由を確認できます(値はマシンによって異なる場合がありますfenv.h。これらの値はglibcを使用するx86-64の場合です)。

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

素晴らしい答えをありがとう。漸近レジームで比較する分析式はexp(log_gamma(m+0.5_dp) - (m+0.5_dp)*log(t)) / 2、m = 234、t = 2000の場合です。私が増加するにつれて、それはすぐにゼロになりmます。私の数値ルーチンが少なくとも12桁の有効数字(ゼロを返すことも完全に適切です)を返すことを確認したいすべてです。したがって、計算が非正規数を返す場合、それは単にゼロであり、問​​題はないはずです。したがって、比較ルーチンだけがこれに対して堅牢である必要があります。
オンドレジ・セティク

5

Donald Knuthは、「The Art of Computer Programming」の第2巻「半数的アルゴリズム」に浮動小数点比較アルゴリズムの提案をしています。それはThによってCで実装されました。Belding(fcmpパッケージを参照)およびGSLで利用可能です。


2
これが私のFortran実装です:gist.github.com/3776847、とにかく非正規数を明示的に処理する必要があることに注意してください。それ以外の場合は、相対エラーとほぼ同じだと思います。唯一の違いはabs(a-b)/max(a, b) < eps、を実行する代わりにを実行することですabs(a-b)/2**exponent(max(a, b)) < eps。これにより、の仮数がかなり下がるmax(a, b)ため、違いは無視できると思います。
オンドレジ・セティク

5

最適に丸められた非正規化数は確かに高い相対誤差を持っている可能性があります。(まだそれを相対エラーと呼んでいる間にゼロにフラッシュすることは誤解を招くものです。)

しかし、ゼロに近いため、相対エラーを計算しても意味がありません。

したがって、非正規化数に達する前であっても、おそらく絶対精度(つまり、この場合は保証したい精度)に切り替える必要があります。

したがって、ような式の有効性をチェックして、計算されたを真のに対してテストすることをお勧めします。relacc = 1e-12およびabsacc = 1e-150と言います。yx|yx|absacc+relaccmax(|x|,|y|)

次に、コードのユーザーは、実際の精度を正確に把握します。


ゼロに近い相対エラーを計算しても意味がないと確信していますか?(何らかの理由で)精度が失われた場合にのみ意味がないと思います。たとえば、いくつかの数値の問題(2つの大きな数値の減算など)により、x <1e-150の精度が失われる場合は、正しいです。しかし、私の場合、数値が非正規数に達した場合を除いて、数値はゼロまでずっと正確であるように見えます。したがって、私の場合、absacc = 1e-320程度で、abs(a-b) < tiny(1._dp)上記と同じように確認できます。
オンドレジ・セティク

@OndřejČertík:その場合、1e-150を1e-300または検証可能な任意の境界に置き換えます。いずれにせよ、ゼロに非常に近い場合は絶対エラーが発生し、エラークレームは相対エラーをゼロであると宣言するのではなく、これを反映する必要があります。
Arnold Neumaier 2012

そうですか。私は確認できます。すべての数値がそれよりも大きいことを確認できますtiny(1._dp)=2.22507385850720138E-308(以前のコメントで間違えました。1e-320ではなく2e-308です)。だからこれは私の絶対的なエラーです。次に、相対誤差を比較する必要があります。私はあなたの主張を理解しています、私はあなたが正しいと思います。ありがとう!
オンドレジ・セティク

1
@OndřejČertík:absaccで指定された追加の相対エラーを見つけるには、の最大値を監視します。|yx|absaccmax(|x|,|y|)
Arnold Neumaier 2012
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.