12

浮動小数点モデル/エラーの問題は非常に紛らわしいと思います。これは私がよく知らない分野であり、低レベルの C/asm プログラマーではないので、アドバイスをいただければ幸いです。

VS2012 (VC11) で構築された大規模な C++ アプリケーションがあり、浮動小数点例外をスローするように構成しました (より正確には、C++ ランタイムやハードウェアが fp 例外をスローできるようにするため)。それらのリリース (最適化) ビルドでは、デバッグ ビルドではありません。これは、最適化とおそらく浮動小数点モデルによるものだと思います (ただし、コンパイラの /fp:precise スイッチはリリース ビルドとデバッグ ビルドの両方に設定されています)。

最初の質問は、アプリのデバッグの管理に関するものです。fp-exceptions がどこでスローされ、どこで「マスク」されるかを制御したいと考えています。これが必要なのは、(最適化された) リリース ビルド (fp 例外が発生する場所) をデバッグしているためです。また、問題を検出した特定の関数で fp 例外を無効にして、新しい FP の問題を特定できるようにする必要があります。しかし、_controlfp_s を使用してこれを行う (正常に動作する) ことと、コンパイラ (および #pragma float_control) スイッチ "/fp:except" (効果がないように見える) の違いに混乱しています。これら2つのメカニズムの違いは何ですか? それらは fp 例外に対して同じ効果を持つはずですか?

次に、GDI+ dll の呼び出しでスローされたように見える例外を含め、多数の「浮動小数点スタック チェック」例外が発生しています。Web を検索すると、この例外についてのいくつかの言及は、コンパイラのバグが原因であることを示しているようです。これは一般的に当てはまりますか?もしそうなら、どうすればこれを回避できますか? 問題のある関数のコンパイラの最適化を無効にするか、問題のあるコードの領域に対してのみ fp-exceptions を無効にするのが最善ですか? たとえば、この例外をスローする GDI+ 呼び出し (GraphicsPath::GetPointCount への) では、実際に返される整数値は正しいようです。現在、_controlfp_s を使用して、GDI+ 呼び出しの直前に fp 例外を無効にしてから、呼び出しの直後に例外を再度有効にするために再度使用しています。

最後に、私のアプリケーションは多くの浮動小数点計算を行い、堅牢で信頼できる必要がありますが、必ずしも非常に正確である必要はありません。アプリケーションの性質上、浮動小数点値は一般に確率を示すため、本質的に多少不正確です。ただし、ゼロ除算などの純粋な論理エラーをトラップしたいと考えています。これに最適な fp モデルは何ですか? 現在、私は:

  • _controlfp_s と SIGFPE シグナル ハンドラを使用して、すべての fp 例外 (つまり、EM_OVERFLOW | EM_UNDERFLOW | EM_ZERODIVIDE | EM_DENORMAL | EM_INVALID) をトラップします。
  • denormals-are-zero (DAZ) と flush-to-zero (FTZ) を有効にしている (つまり、_MM_SET_FLUSH_ZERO_MODE(_MM_DENORMALS_ZERO_ON))、および
  • デフォルトの VC11 コンパイラ設定 /fp:precise と /fp:except を使用しています。

これは最高のモデルですか?

よろしくお願いします!

4

3 に答える 3

4

以下の情報のほとんどは、この件に関する Bruce Dawson のブログ投稿 (リンク) からのものです。

C++ を使用しているため、浮動小数点例外を有効または無効にする RAII クラスをスコープ付きで作成できます。これにより、_controlfp_s() の呼び出しを自分で手動で管理するのではなく、コードに例外状態のみを公開するように、より細かく制御できます。さらに、このように設定された浮動小数点の例外状態はシステム全体に及ぶため、コントロール ワードの以前の状態を覚えておき、必要に応じて復元することをお勧めします。RAII はこれを処理することができ、説明している GDI+ の問題に対する優れたソリューションです。

例外フラグ _EM_OVERFLOW、_EM_ZERODIVIDE、および _EM_INVALID は、考慮すべき最も重要なものです。_EM_OVERFLOW は、正または負の無限大が計算の結果である場合に発生しますが、_EM_INVALID は、結果がシグナリング NaN である場合に発生します。_EM_UNDERFLOW は無視しても安全です。計算結果がゼロ以外で、-FLT_MIN と FLT_MIN の間にある場合 (つまり、非正規化を生成した場合) に通知します。_EM_INEXACT は、浮動小数点演算の性質上、実際に使用するにはあまりにも頻繁に発生しますが、状況によっては不正確な結果を追跡しようとする場合に役立ちます。

SIMD コードは、ミックスにさらにしわを寄せます。SIMD を明示的に使用することを示していないため、/fp:fast 以外のものを指定すると、VS 2012 でコードの自動ベクトル化が無効になる可能性があることに注意してください。詳細については、この回答を参照してください。

于 2013-07-25T03:51:16.340 に答える
1

最初の 2 つの質問についてはあまり役に立ちませんが、FPU 例外のマスキングに関する質問については経験と提案があります。

機能を見つけました

_statusfp()  (x64 and Win32)
_statusfp2() (Win32 only)
_fpreset()
_controlfp_s()
_clearfp()
_matherr()

FPU 例外をデバッグするとき、および安定した高速な製品を提供するときに役立ちます。

デバッグ時には、例外を選択的にマスク解除して、計算で fpu 例外が生成されるコード行を分離するのに役立ちます。ここでは、fpu 例外を予期せず生成する他のコードの呼び出しを避けることができません (.NET JIT のゼロ除算など)。

リリースされた製品では、これらを使用して、重大な浮動小数点例外を許容し、例外が発生したときに検出し、正常に回復できる安定したプログラムを提供しています。

変更できないコードを呼び出す必要がある場合、すべての FPU 例外をマスクし、信頼できる例外処理を行わず、FPU 例外を時折生成します。

例:

#define BAD_FPU_EX (_EM_OVERFLOW | _EM_ZERODIVIDE | _EM_INVALID)
#define COMMON_FPU_EX (_EM_INEXACT | _EM_UNDERFLOW | _EM_DENORMAL)
#define ALL_FPU_EX (BAD_FPU_EX | COMMON_FPU_EX)

リリースコード:

_fpreset();
Use _controlfp_s() to mask ALL_FPU_EX 
_clearfp();
... calculation
unsigned int bad_fpu_ex = (BAD_FPU_EX  & _statusfp());
_clearfp(); // to prevent reacting to existing status flags again
if ( 0 != bad_fpu_ex )
{
  ... use fallback calculation
  ... discard result and return error code
  ... throw exception with useful information
}

デバッグ コード:

_fpreset();
_clearfp();
Use _controlfp_s() to mask COMMON_FPU_EX and unmask BAD_FPU_EX 
... calculation
  "crash" in debugger on the line of code that is generating the "bad" exception.

コンパイラ オプションによっては、リリース ビルドが FPU ops への組み込み呼び出しを使用している場合があり、デバッグ ビルドが数学ライブラリ関数を呼び出している場合があります。これら 2 つのメソッドは、sqrt(-1.0) のような無効な操作のエラー処理動作が大きく異なる場合があります。

64 ビット Windows 7 で VS2010 でビルドされた実行可能ファイルを使用して、Win32 プラットフォームと x64 プラットフォームで同じコードを使用すると、わずかに異なる倍精度演算値が生成されました。/fp::precise を使用して最適化されていないデバッグ ビルドを使用しても、fpu 精度制御は明示的に _PC_53 に設定され、fpu 丸め制御は明示的に _RC_NEAR に設定されます。プラットフォームを考慮して、倍精度値を比較するいくつかの回帰テストを調整する必要がありました。これが VS2012 の問題であるかどうかはわかりませんが、注意してください。

于 2013-07-24T20:49:13.770 に答える
0

Linux での浮動小数点例外の処理に関する情報を取得するのに苦労してきましたが、学んだことをお伝えできます。例外メカニズムを有効にする方法はいくつかあります。

  1. fesetenv (FE_NOMASK_ENV); すべての例外を有効にします
  2. fenableexcept(FE_ALL_EXCEPT );
 fpu_control_t fw;
 _FPU_GETCW(fw);
 fw |=FE_ALL_EXCEPT;
 _FPU_SETCW(fw);

4.

> fenv_t envp; include bits/fenv.h   
> fegetenv(&envp);    
 envp.__control_word |= ~_FPU_MASK_OM;   
> fesetenv(&envp);

5.

> fpu_control_t cw;
> __asm__ ("fnstcw %0" : "=m" (*&cw));get config word
>cw |= ~FE_UNDERFLOW;
> __asm__ ("fldcw %0" : : "m" (*&cw));write config word

6.C++ モード: std::feclearexcept(FE_ALL_EXCEPT);

いくつかの便利なリンクがあります: http://frs.web.cern.ch/frs/Source/MAC_headers/fpu_control.h http://en.cppreference.com/w/cpp/numeric/fenv/fetestexcept http:// technopark02.blogspot.ro/2005/10/handling-sigfpe.html

于 2013-06-16T11:20:16.910 に答える