Oracle® Developer Studio 12.5: 数値計算ガイド

印刷ビューの終了

更新: 2016 年 6 月
 
 

4.3 例外の検出

IEEE 規格の要求に従い、SPARC ベースのシステムおよび x86 ベースのシステムの浮動小数点環境では、浮動小数点例外の発生を記録するステータスフラグが提供されています。プログラムでこれらのフラグをテストすると、発生した例外を判別できます。これらのフラグは、明示的に設定およびクリアすることもできます。ieee_flags 関数は、これらのフラグにアクセスする方法の 1 つです。C または C++ で記述されるプログラムでは、C99 浮動小数点環境関数によって別の方法が提供されています。

SPARC ベースのシステムでは、各例外には 「現在」と 「累積」の 2 つのフラグが関連付けられています。現在の例外フラグは、最後の浮動小数点命令の実行が完了したことによって発生した例外を常に示しています。これらのフラグは累積例外フラグにも累積され (つまり、論理和が生成されます)、それにより、プログラムの実行が開始されてから、またはプログラムによって累積フラグが最後にクリアされてから発生し、まだトラップされていないすべての例外が記録されます。浮動小数点命令がトラップされた例外の原因である場合、そのトラップを発生させた例外に対応する現在の例外フラグが設定されますが、累積フラグは変更されません。現在の例外フラグと累積例外フラグは、浮動小数点ステータスレジスタ %fsr 内に保持されます。

x86 ベースのシステムでは、浮動小数点ステータスワード (SW) が累積例外のフラグ、および浮動小数点スタックのステータスのフラグを提供します。SSE2 命令をサポートする x86 ベースのシステムでは、それらの命令によって発生した累積例外を記録するフラグが MXCSR レジスタに含まれています。

4.3.1 ieee_flags(3m)

ieee_flags は、Oracle Developer Studio C、C++、および Fortran に似ている IEEE 754 例外フラグのインタフェースを提供しています。ただし、このインタフェースは Oracle Solaris OS でのみ使用できます。より幅広く移植できるようにする C および C++ プログラムには、C99 例外フラグ関数を使用してください。

ieee_flags(3m) を呼び出す構文は次のとおりです。

i = ieee_flags(action, mode, in, out);

プログラムで累積例外ステータスフラグをテスト、設定、またはクリアするには、文字列 "exception" を 2 番目の引数として指定して ieee_flags 関数を使用します。たとえば、Fortran でオーバーフロー例外フラグをクリアするには、次のように記述します。

      character*8 out
      call ieee_flags('clear', 'exception', 'overflow', out) 

C または C++ で、例外が発生したかどうかを判別するには、次のように記述します。

      i = ieee_flags("get", "exception", in, out); 

action が "get" の場合に out に返される文字列は、次のいずれかです。

  • "not available" — 例外に関する情報を取得できない場合

  • "" (空白の文字列) — 累積例外が一度も発生していない場合、または x86 の場合は、非正規オペランドが唯一の累積例外であるとき

  • 例外が発生した場合は、3 番目の引数 in に指定されている例外の名前が返されます

  • そうでない場合は、発生したもっとも高い優先順位を持つ例外の名前

たとえば、次の Fortran の呼び出しで 0 による除算の例外が発生した場合、out に返される文字列は "division" になります。それ以外の場合は、発生したもっとも優先順位の高い例外の名前です。

      character*8 out
      i = ieee_flags('get', 'exception', 'division', out)

in に特定の例外が指定されてない場合、in は無視されます。たとえば、次の C の呼び出しでは引数 "all" は無視されます。

      i = ieee_flags("get", "exception", "all", out); 

out に例外の名前を返すことに加えて、ieee_flags は、現在発生している例外フラグをすべて組み合わせた整数値も返します。この値は、すべての累積例外フラグのビット単位の「or」であり、各フラグは表 32 に示されているように単一のビットで表されます。各例外に対応するビットの位置は、sys/ieeefp.h ファイルに定義されている fp_exception_type 値によって示されています(これらのビットの位置はマシンによって異なり、連続しているとは限りません)。

表 32  例外ビット
例外
ビットの位置
累積例外ビット
invalid
fp_invalid
i & (1 << fp_invalid)
overflow
fp_overflow
i & (1 << fp_overflow)
division
fp_division
i & (1 << fp_division)
underflow
fp_underflow
i & (1 << fp_underflow)
inexact
fp_inexact
i & (1 << fp_inexact)
denormalized
fp_denormalized
i & (1 << fp_denormalized) (x86 のみ)

次の C または C++ のプログラムの一部は、戻り値をデコードする 1 つの方法を示しています。

/*
 *   Decode integer that describes all accrued exceptions. 
 *   fp_inexact etc. are defined in <sys/ieeefp.h> 
 */ 
 
char *out; 
int invalid, division, overflow, underflow, inexact; 
 
code = ieee_flags("get", "exception", "", &out); 
printf ("out is %s, code is %d, in hex: 0x%08X\n", 
  out, code, code); 
inexact  =  (code >> fp_inexact) & 0x1; 
division  =  (code >> fp_division) & 0x1; 
underflow  =  (code >> fp_underflow) & 0x1; 
overflow  =  (code >> fp_overflow) & 0x1; 
invalid  =  (code >> fp_invalid) & 0x1; 
printf("%d %d %d %d %d \n", invalid, division, overflow,
  underflow, inexact); 

4.3.2 C99 例外フラグ関数

C/C++ プログラムでは、C99 浮動小数点環境関数を使用して、浮動小数点の例外フラグをテスト、設定、およびクリアできます。ヘッダーファイル fenv.h は、5 つの標準例外 (FE_INEXACTFE_UNDERFLOWFE_OVERFLOWFE_DIVBYZERO、および FE_INVALID) に対応する 5 つのマクロを定義しています。fenv.h は、マクロ FE_ALL_EXCEPT が 5 つの例外マクロすべてのビット単位の「or」となるようにも定義しています。これらのマクロを組み合わせることにより、例外フラグの任意のサブセットのテストやクリアを行なったり、例外の任意の組み合わせを発生させたりできます。次に、これらのマクロを C99 浮動小数点環境関数のいくつかと合わせて使用した例を示します。詳細は、feclearexcept(3M) のマニュアルページを参照してください。


注 -  一貫した動作を保つため、libm の C99 浮動小数点環境関数と拡張機能、および libsunmathieee_flags 関数と ieee_handler 関数の両方を同じプログラム内で使用しないでください。

5 つの例外フラグすべてをクリアするには、次を使用します。

feclearexcept(FE_ALL_EXCEPT);

無効な演算フラグまたは 0 による除算フラグが発生したかどうかをテストするには、次を使用します。

int i;

i = fetestexcept(FE_INVALID | FE_DIVBYZERO);
if (i & FE_INVALID)
    /* invalid flag was raised */
else if (i & FE_DIVBYZERO)
    /* division-by-zero flag was raised */

fegetexceptflag 関数および fesetexceptflag 関数は、フラグのサブセットを保存および復元する方法を提供しています。次の例は、これらの 2 つの関数を使用する 1 つの方法を示しています。

fexcept_t flags;
 
/* save the underflow, overflow, and inexact flags */
fegetexceptflag(&flags, FE_UNDERFLOW | FE_OVERFLOW | FE_INEXACT);
/* clear these flags */
feclearexcept(FE_UNDERFLOW | FE_OVERFLOW | FE_INEXACT);
/* do a computation that can underflow or overflow */
...
/* check for underflow or overflow */
if (fetestexcept(FE_UNDERFLOW | FE_OVERFLOW) != 0) {
    ...
}
/* restore the underflow, overflow, and inexact flags */
fesetexceptflag(&flags, FE_UNDERFLOW | FE_OVERFLOW, | FE_INEXACT);