C ++関数の数値エラーの分析


20

入力として複数の浮動小数点値(単一または二重)を受け取り、何らかの計算を行い、出力浮動小数点値(単一または二重)を生成する関数があるとします。私は主にMSVC 2008で作業していますが、MinGW / GCCでも作業する予定です。私はC ++でプログラミングしています。

結果に含まれるエラーの量をプログラムで測定する一般的な方法は何ですか?私は任意精度ライブラリを使用する必要があると仮定します:速度を気にしない場合、そのような最適なライブラリは何ですか?

回答:


17

丸め誤差の適切な範囲を探している場合、必ずしもaribtrary-precisionライブラリは必要ありません。代わりに、実行中のエラー分析を使用できます。

優れたオンラインリファレンスを見つけることができませんでしたが、Nick Highamの本「数値アルゴリズムの精度と安定性」のセクション3.3ですべて説明されています。アイデアは非常に簡単です。

  1. コードをリファクタリングして、各行に単一の算術演算の単一の割り当てがあるようにします。
  2. 各変数に対して、たとえばx、定数が割り当てられたx_errときにゼロに初期化される変数を作成しますx
  3. 各操作に対して、たとえばz = x * yz_err浮動小数点演算の標準モデルと結果のzエラーx_errおよび実行中のエラーを使用して変数を更新しますy_err
  4. 関数の戻り値には、それぞれの_err値も添付されている必要があります。これは、総丸め誤差のデータ依存の限界です。

難しい部分はステップ3です。最も単純な算術演算では、次の規則を使用できます。

  • z = x + y -> z_err = u*abs(z) + x_err + y_err
  • z = x - y -> z_err = u*abs(z) + x_err + y_err
  • z = x * y -> z_err = u*abs(z) + x_err*abs(y) + y_err*abs(x)
  • z = x / y -> z_err = u*abs(z) + (x_err*abs(y) + y_err*abs(x))/y^2
  • z = sqrt(x) -> z_err = u*abs(z) + x_err/(2*abs(z))

u = eps/2単位の丸めはどこですか。はい、のルール+とルール-は同じです。にop(x)適用される結果のテイラー級数展開を使用して、他の操作のルールを簡単に抽出できますop(x + x_err)。または、グーグルで試すことができます。または、Nick Highamの本を使用します。

例として、ホーナースキームを使用aしてポイントで係数の多項式を評価する次のMatlab / Octaveコードを考えますx

function s = horner ( a , x )
    s = a(end);
    for k=length(a)-1:-1:1
        s = a(k) + x*s;
    end

最初のステップでは、次の2つの操作を分割しs = a(k) + x*sます。

function s = horner ( a , x )
    s = a(end);
    for k=length(a)-1:-1:1
        z = x*s;
        s = a(k) + z;
    end

次に、_err変数を紹介します。入力axは正確であると想定されていることに注意してください。しかし、a_errとに対応する値を渡すようにユーザーに要求することもできますx_err

function [ s , s_err ] = horner ( a , x )
    s = a(end);
    s_err = 0;
    for k=length(a)-1:-1:1
        z = x*s;
        z_err = ...;
        s = a(k) + z;
        s_err = ...;
    end

最後に、上記のルールを適用してエラー条件を取得します。

function [ s , s_err ] = horner ( a , x )
    u = eps/2;
    s = a(end);
    s_err = 0;
    for k=length(a)-1:-1:1
        z = x*s;
        z_err = u*abs(z) + s_err*abs(x);
        s = a(k) + z;
        s_err = u*abs(s) + z_err;
    end

a_error がないためx_err、たとえばゼロであると想定されるため、それぞれの項はエラー式で単純に無視されます。

ほら!結果に加えて、データ依存のエラー推定値(注:これはエラーの上限)を返すHornerスキームがあります。

補足として、C ++を使用しているため、浮動小数点値用の独自のクラスを作成して_err用語を実行し、すべての算術演算をオーバーロードしてこれらの値を上記のように更新することを検討できます。大きなコードの場合、これは計算上効率的ではありませんが、より簡単なルートです。そうは言っても、そのようなクラスをオンラインで見つけることができるかもしれません。簡単なGoogle検索でこのリンクが表示されました

±あなたはバツ1±あなたは


1
興味深いので、この分析のために+1。私はハイアムの作品が好きです。私が懸念しているのは、数値演算の数が多くなると、ユーザーが(半自動で間隔演算のように)余分なコードを書く必要があることは、エラーを起こしやすいことです。
ジェフオックスベリー

1
@GeoffOxberry:複雑さの問題に完全に同意します。大きなコードの場合、各操作を1回だけ正しく実装する必要があるなど、doubleの操作をオーバーロードするクラス/データ型を記述することを強くお勧めします。Matlab / Octaveにはこのようなものが存在しないように思えることに非常に驚いています。
ペドロ

私はこの分析が好きですが、エラー項の計算も浮動​​小数点で実行されるので、それらのエラー項は浮動小数点エラーのために不正確ではないでしょうか?
plasmacel

8

任意精度の浮動小数点演算(およびその他の多く)に対応する、移植性の高いオープンソースライブラリは、Victor ShoupのNTLであり、C ++ソース形式で入手できます。

下位レベルには、GNU Multiple Precision(GMP)Bignum Libraryがあり、これもオープンソースパッケージです。

NTLはGMPで使用できますが、高速なパフォーマンスが必要ですが、NTLには独自の基本ルーチンがあり、「速度を気にしない」場合に確実に使用できます。GMPは「最速のbignumライブラリ」であると主張しています。GMPは主にCで書かれていますが、C ++インターフェイスを備えています。

追加:区間演算は自動化された方法で正確な答えに上限と下限を与えることができますが、これは「標準」精度計算でエラーを正確に測定しません。絶対エラーセンス)。

丸め誤差または離散化エラーなどのエラーサイズを見つける一般的な方法は、追加の精度値を計算し、それを「標準」精度値と比較することです。誤差の大きさ自体を妥当な精度で決定するには、控えめな精度のみが必要です。これは、丸め誤差だけが、余分な精度の計算よりも「標準」精度が大幅に大きいためです。

ポイントは、単精度計算と倍精度計算を比較することで説明できます。C ++では、中間式は常に(少なくとも)倍精度で計算されるため、「純粋な」単精度での計算がどのようなものかを説明する場合は、中間値を単精度で保存する必要があります。

Cコードスニペット

    float fa,fb;
    double da,db,err;
    fa = 4.0;
    fb = 3.0;
    fa = fa/fb;
    fa -= 1.0;

    da = 4.0;
    db = 3.0;
    da = da/db;
    da -= 1.0;

    err = fa - da;
    printf("Single precision error wrt double precision value\n");
    printf("Error in getting 1/3rd is %e\n",err);
    return 0;

上記の出力(Cygwin / MinGW32 GCCツールチェーン):

Single precision error wrt double precision value
Error in getting 1/3rd is 3.973643e-08

したがって、エラーは、1/3を単精度に丸める際に予想されるものです。エラーの測定は正確さではなく大きさのためであるため、エラー修正で小数点以下数桁以上を取得することは気にしません(私は疑います)。


あなたのアプローチは間違いなく数学的に健全です。トレードオフは厳密だと思います。エラーについて熱心な人は、区間演算の厳密さを指摘しますが、多くのアプリケーションでは、余分な精度で計算すれば十分であり、指摘するように、結果として生じるエラー推定はより厳しくなると思います。
ジェフオックスベリー

これは、私が使用することを想像していたアプローチです。これらのさまざまな手法のいくつかを試して、どれが自分のアプリケーションに最も適しているかを確認します。コード例の更新は大歓迎です!
user_123abc

7

GMP(GNU Multiple Precision Library)は、私が知っている最高の任意精度ライブラリです。

任意の浮動小数点関数の結果のエラーを測定するプログラム的な方法は知りません。試行できることの1つは、区間演算を使用して関数の区間拡張を計算することです。C ++では、インターバル拡張を計算するために何らかのライブラリを使用する必要があります。そのようなライブラリの1つが、ブースト間隔演算ライブラリです。。基本的に、エラーを測定するには、関数の間隔に引数として2倍の単位の丸めの幅(大まかに)を指定し、目的の値を中心にすると、出力は間隔のコレクションになります。エラーの控えめな見積もりが得られます。この方法の難点は、この方法で使用される区間演算がかなりの量の誤差を過大評価する可能性があることですが、この方法は私が考えることができる最も「プログラム的な」方法です。


ああ、私はあなたの答えで言及された間隔計算に気づいた...賛成!
アリ

2
リチャードハリスはACCUジャーナルOverloadFloating Point Bluesについて素晴らしいシリーズを書いた。区間演算に関する彼の記事は、オーバーロード103pdf、p19-24)にあります。
マークブース

6

間隔分析により、厳密で自動のエラー推定を実現できます。数値ではなく間隔を使用します。追加の例:

[a,b] + [c,d] = [min(a+c, a+d, b+c, b+d), max (a+c, a+d, b+c, b+d)] = [a+c, b+d]

四捨五入も厳密に処理できます。「四捨五入間隔演算」を参照してください。

入力が狭い間隔で構成されている限り、推定値は問題なく、計算コストは​​安くなります。残念ながら、エラーはしばしば過大評価されます依存関係の問題を参照してください。

任意の精度の区間演算ライブラリーは知りません。

間隔計算がニーズを満たすかどうかは、手元の問題に依存します。


4

GNU MPFRライブラリは、その主なフォーカスポイントの一つとして、(同じくらい簡単に聞こえるようではないすべての操作のために、特に、正しい丸め)高い精度を有する任意精度浮動小数点ライブラリです。内部でGNU MPを使用します。間隔演算を行うMPFIと呼ばれる拡張機能があり、Geoffの答えが示唆するように、検証の目的で役立ちます。結果の間隔が小さい範囲内になるまで作業精度を上げ続けます。

ただし、これは常に機能するとは限りません。特に、すべてのステップが丸め問題とは無関係に「エラー」を伴う数値積分のようなものを実行している場合、必ずしも効果的ではありません。その場合、COZY infinityなどの特殊なパッケージを試してみてください。これは、特定のアルゴリズムを使用して積分誤差を制限する(および間隔ではなくいわゆるTaylorモデルを使用する)ことで非常にうまく機能します。


同意する; 数値積分は、ナイーブな区間演算がうまくいかない場合があります。ただし、Taylorモデルでさえ区間演算を使用します。私は牧野とベルツの作品に精通しており、REムーアの意味でテイラーモデルを使用していると思いますが、「微分代数」と呼ばれるものを含むトリックも使用しています。
ジェフオックスベリー

@GeoffOxberry:ええ-この微分代数は、統合ステップでのエラーに限界があると思います。
エリックP.

0

Visual Studioを使用している場合、MPIRは優れたライブラリであると言われています。

http://mpir.org/


SciComp.SEへようこそ!このライブラリを使用して浮動小数点計算のエラーを測定する方法について詳しく説明してください。
クリスチャンクラソン

試してみます; 私は実際にまだコンピューター上でMPIRをまだセットアップしていません!GMPとMPFRをセットアップしました。
フィッシャーマン
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.