double
一部の数値結果 (およびを使用した浮動小数点演算を含むfloat
) が大きな入力サイズでは不正確になるが、小さな入力サイズでは不正確になる状況があります。
一般的に、数値のオーバーフローや問題のある精度の低下などの状態を診断するために使用できるツールを知りたいです。
言い換えれば、valgrind がメモリエラーについて不平を言うのと同じように、オーバーフローなどについて不平を言うツールはありますか?
double
一部の数値結果 (およびを使用した浮動小数点演算を含むfloat
) が大きな入力サイズでは不正確になるが、小さな入力サイズでは不正確になる状況があります。
一般的に、数値のオーバーフローや問題のある精度の低下などの状態を診断するために使用できるツールを知りたいです。
言い換えれば、valgrind がメモリエラーについて不平を言うのと同じように、オーバーフローなどについて不平を言うツールはありますか?
浮動小数点例外を有効にすると、FPU はオーバーフロー時に例外をスローできます。これがどのように機能するかは、オペレーティング システムによって異なります。例えば:
feenableexcept(FE_ALL_EXCEPT)
は、main
. オーバーフローとゼロ除算を有効にするには、 を呼び出しますfeenableexcept(FE_OVERFLOW | FE_DIVBYZERO)
。いずれの場合も、有効にした例外がサードパーティのコードによって無効になる可能性があることに注意してください。これはおそらく実際にはまれです。
これはおそらく Valgrind ほど優れたものではありません。これは、最後に要約を取得するというよりも、デバッガーにドロップして手動で検査する方が多いためですが、機能します。
オーバーフローを診断するには、浮動小数点例外を使用できます。たとえば、cppreferenceを参照してください。浮動小数点エラーの動作を構成するには、実装固有の関数を使用する必要がある場合があることに注意してください。
それらはしばしば「例外」と呼ばれますが、浮動小数点エラーはC++例外を引き起こさないことに注意してください。
cppreferenceコードは、IEEE754に基づく実装のデフォルトの動作を示しています。適切であると判断した場合はいつでも浮動小数点例外フラグを確認できます。計算を入力する前に、フラグをクリアする必要があります。計算が完了するまで待って、フラグが設定されているかどうかを確認するか、エラーの影響を受けやすいと思われるすべての操作をチェックすることをお勧めします。
そのような「例外」が無視できない何かをトリガーするようにするための実装固有の拡張機能があるかもしれません。Windows / MSVC ++では「構造化例外」(実際のC ++ではない)である可能性があり、LinuxではSIGFPEである可能性があります(したがって、エラーを処理するためのシグナルハンドラーが必要です)。このような動作を有効にするには、実装固有のライブラリ関数またはコンパイラ/リンカフラグが必要です。
オーバーフローが問題になる可能性は低いと思います。一部の入力が大きくなり、他の値は小さいままの場合、それらを組み合わせるときに精度が低下する可能性があります。これを制御する1つの方法は、区間演算を使用することです。そのためのさまざまなライブラリがあり、ブースト間隔も含まれます。
免責事項:私はこのライブラリ(または他の区間演算ライブラリ)の経験はありませんが、おそらくこれで始めることができます。
In addition to the excellent suggestions already posted, here is another approach. Write a function that examines your floating point data structures, doing range and consistency checks. Insert calls to it in your main loop. In order to examine other variables you can set a breakpoint in the checker after it has found a problem.
This is more set-up work than enabling exceptions, but can pick up subtler problems such as inconsistencies and numbers that are larger than expected without having gone infinite, leading to detection close to the original problem.
コーディングミスを犯した可能性があるアルゴリズムの実装をデバッグする必要があり、実行中の浮動小数点計算を追跡したい場合があります。操作対象のすべての値を調べて、期待する範囲外にあると思われる値を探すためのフックが必要になる場合があります。C++ ではfloating point
、すべての計算を検査する機能を保持しながら、独自のクラスを定義し、演算子のオーバーロードを使用して自然な方法で計算を記述することができます。
たとえば、FP
クラスを定義し、すべての加算と乗算を出力するプログラムを次に示します。
#include <iostream>
struct FP {
double value;
FP( double value ) : value(value) {}
};
std::ostream & operator<< ( std::ostream &o, const FP &x ) { o << x.value; return o; }
FP operator+( const FP & lhs, const FP & rhs ) {
FP sum( lhs.value + rhs.value );
std::cout << "lhs=" << lhs.value << " rhs=" << rhs.value << " sum=" << sum << std::endl;
return sum;
}
FP operator*( const FP & lhs, const FP & rhs ) {
FP product( lhs.value * rhs.value );
std::cout << "lhs=" << lhs.value << " rhs=" << rhs.value << " product=" << product << std::endl;
return product;
}
int main() {
FP x = 2.0;
FP y = 3.0;
std::cout << "answer=" << x + 2 * y << std::endl;
return 0;
}
どのプリント
lhs=2 rhs=3 product=6
lhs=2 rhs=6 sum=8
answer=8
更新:各浮動小数点演算の後に浮動小数点ステータス フラグを表示するように (x86 で) プログラムを拡張しました (加算と乗算のみを実装し、他のものは簡単に追加できます)。
#include <iostream>
struct MXCSR {
unsigned value;
enum Flags {
IE = 0, // Invalid Operation Flag
DE = 1, // Denormal Flag
ZE = 2, // Divide By Zero Flag
OE = 3, // Overflow Flag
UE = 4, // Underflow Flag
PE = 5, // Precision Flag
};
};
std::ostream & operator<< ( std::ostream &o, const MXCSR &x ) {
if (x.value & (1<<MXCSR::IE)) o << " Invalid";
if (x.value & (1<<MXCSR::DE)) o << " Denormal";
if (x.value & (1<<MXCSR::ZE)) o << " Divide-by-Zero";
if (x.value & (1<<MXCSR::OE)) o << " Overflow";
if (x.value & (1<<MXCSR::UE)) o << " Underflow";
if (x.value & (1<<MXCSR::PE)) o << " Precision";
return o;
}
struct FP {
double value;
FP( double value ) : value(value) {}
};
std::ostream & operator<< ( std::ostream &o, const FP &x ) { o << x.value; return o; }
FP operator+( const FP & lhs, const FP & rhs ) {
FP sum( lhs.value );
MXCSR mxcsr, new_mxcsr;
asm ( "movsd %0, %%xmm0 \n\t"
"addsd %3, %%xmm0 \n\t"
"movsd %%xmm0, %0 \n\t"
"stmxcsr %1 \n\t"
"stmxcsr %2 \n\t"
"andl $0xffffffc0,%2 \n\t"
"ldmxcsr %2 \n\t"
: "=m" (sum.value), "=m" (mxcsr.value), "=m" (new_mxcsr.value)
: "m" (rhs.value)
: "xmm0", "cc" );
std::cout << "lhs=" << lhs.value
<< " rhs=" << rhs.value
<< " sum=" << sum
<< mxcsr
<< std::endl;
return sum;
}
FP operator*( const FP & lhs, const FP & rhs ) {
FP product( lhs.value );
MXCSR mxcsr, new_mxcsr;
asm ( "movsd %0, %%xmm0 \n\t"
"mulsd %3, %%xmm0 \n\t"
"movsd %%xmm0, %0 \n\t"
"stmxcsr %1 \n\t"
"stmxcsr %2 \n\t"
"andl $0xffffffc0,%2 \n\t"
"ldmxcsr %2 \n\t"
: "=m" (product.value), "=m" (mxcsr.value), "=m" (new_mxcsr.value)
: "m" (rhs.value)
: "xmm0", "cc" );
std::cout << "lhs=" << lhs.value
<< " rhs=" << rhs.value
<< " product=" << product
<< mxcsr
<< std::endl;
return product;
}
int main() {
FP x = 2.0;
FP y = 3.9;
std::cout << "answer=" << x + 2.1 * y << std::endl;
std::cout << "answer=" << x + 2 * x << std::endl;
FP z = 1;
for( int i=0; i<310; ++i) {
std::cout << "i=" << i << " z=" << z << std::endl;
z = 10 * z;
}
return 0;
}
最後のループは、10
オーバーフローが発生したことを示すのに十分な回数で数値を乗算します。精度エラーも発生することに気付くでしょう。オーバーフローすると値が無限大で終了します。
ここに出力の末尾があります
lhs=10 rhs=1e+305 product=1e+306 Precision
i=306 z=1e+306
lhs=10 rhs=1e+306 product=1e+307
i=307 z=1e+307
lhs=10 rhs=1e+307 product=1e+308 Precision
i=308 z=1e+308
lhs=10 rhs=1e+308 product=inf Overflow Precision
i=309 z=inf
lhs=10 rhs=inf product=inf