cc [ flag ... ] file ... -lsunmath -lm [ library ... ] #include <sunmath.h> long ieee_handler(const char *action, const char *exception, sigfpe_handler_type hdl);
この関数は、ANSI/IEEE 規格 754-1985 で定義されている 5 つの浮動小数点例外に対して、同時に SIGFPE シグナルハンドラを確立し、浮動小数点トラップ有効モードを管理します。action と exception 引数は、実行する演算を指定するための大文字と小文字が区別されない文字列へのポインタです。無効な引数文字列を指定したり、引数を無効な組み合わせで指定したりすると、未定義の結果が生じます。
action には、“get”、“set”、“clear” という 3 つの有効な値があり、exception には次の 7 つの有効な値があります。
“inexact” “division” ... division by zero exception “underflow” “overflow” “invalid” “all” ... all five exceptions above “common” ... invalid, overflow, and division exceptions
action が “set” の場合、ieee_handler() は指定された例外に対応するハンドラ hdl を確立します。hdl には、SIGFPE_DEFAULT、SIGFPE_IGNORE、SIGFPE_ABORT のいずれか、またはユーザー定義の SIGFPE ハンドラのアドレスを指定できます。(マクロ SIGFPE_DEFAULT、SIGFPE_IGNORE、SIGFPE_ABORT および型 sigfpe_handler_type は、<floatingpoint.h> に定義されています。)
hdl が SIGFPE_DEFAULT または SIGFPE_IGNORE の場合、ieee_handler() は指定された例外のトラップを無効にします。その例外が次に発生した場合は、IEEE 754 に規定されているデフォルトの動作が実行されます。
hdl が SIGFPE_ABORT の場合、ieee_handler() は指定された例外のトラップを有効にします。その例外が次に発生した場合は、プログラムは abort(3c) を呼び出してコアダンプを出力します。
hdl がユーザー定義の SIGFPE ハンドラのアドレスの場合の場合、ieee_handler() は指定された例外のトラップを有効にします。その例外が次に発生した場合は、SA_SIGINFO フラグが設定された sigaction(2) を使用してインストールされた場合と同様に、ハンドラが呼び出されます。
action が “clear” の場合、ieee_handler() は指定された例外のトラップを無効にし、対応するハンドラを SIGFPE_DEFAULT に設定します。
action が “get” の場合、ieee_handler() は指定された例外に関連付けられたハンドラを返します (型 long にキャストする)。hdl 引数は無視されます。
“get” 以外のアクションの場合、ieee_handler() は、要求されたアクションを実行できない場合 (ベースとなるハードウェアで、指定された例外のトラップを有効または無効にすることがサポートされていない場合など) は 1 を返し、それ以外の場合は 0 を返します。
ieee_handler() を使用して浮動小数点例外で発生した SIGFPE シグナルをキャッチするには、プログラムで次の処理を実行する必要があります。
ieee_handler を使用してハンドラを設定します。
目的の例外を生成する浮動小数点演算を実行します。
次のプログラムは、無効な演算例外用のハンドラを設定し、0/0 の計算を試みて例外を発生させます。その後、ハンドラは、この演算について IEEE 754 に規定されているデフォルトの結果に大域変数の値を代入します。.下記のハンドラは、SPARC システム上でプログラムが SPARC V8 命令セット用にコンパイルされている場合にのみ機能します。
/* * Sample floating point exception handler. In this example, we trap * on the invalid operation exception. The handler checks that the * exception occurred as a result of an attempt to compute 0/0. If so, * it supplies the value of the global variable zero_over_zero_value * for the result of the instruction that raised the exception. */ #include <stdio.h> #include <stdlib.h> #include <math.h> #include <sunmath.h> #include <siginfo.h> #include <ucontext.h> double zero_over_zero_value; void zero_over_zero_handler(int sig, siginfo_t *sip, ucontext_t *uap) { /* see <sys/reg.h> for structure fpregset_t */ fpregset_t *uc = &uap->uc_mcontext.fpregs; int i, j, fop, frd, frs1, frs2; int *con = (int *) &zero_over_zero_value; /* * decode the trapping instruction to determine the * operation and source and destination registers */ fop = (uc->fpu_q->FQu.fpq.fpq_instr >> 5) & 0x1ff; frd = (uc->fpu_q->FQu.fpq.fpq_instr >> 25) & 0x1f; frs1 = (uc->fpu_q->FQu.fpq.fpq_instr >> 14) & 0x1f; frs2 = uc->fpu_q->FQu.fpq.fpq_instr & 0x1f; /* * check if both rs1 and rs2 are zero (0/0 case) */ i = (uc->fpu_fr.fpu_regs[frs2] & 0x7fffffff) | uc->fpu_fr.fpu_regs[frs2+1]; j = (uc->fpu_fr.fpu_regs[frs1] & 0x7fffffff) | uc->fpu_fr.fpu_regs[frs1+1]; if (fop == 0x4e) /* instruction is fdivd */ if ((i | j) == 0) { /* both operands are 0 */ /* set rd to zero_over_zero_value */ uc->fpu_fr.fpu_regs[frd] = con[0]; uc->fpu_fr.fpu_regs[frd+1] = con[1]; } } } int main(void) { double x, w; int i, k; sigfpe_handler_type hdl, oldhdl; /* * save current invalid operation handler */ oldhdl = (sigfpe_handler_type) ieee_handler("get", "invalid", (sigfpe_handler_type)0); /* * set up new handler */ hdl = (sigfpe_handler_type) zero_over_zero_handler; (void) ieee_handler("set", "invalid", hdl); /* * compute (k*x)/sin(x) for k=2, x=0.5, 0.4, ..., 0.1, 0.0 */ k = 2.0; (void) printf("Evaluating f(x) = (k*x)/sin(x)\n\n"); zero_over_zero_value = k; for (i = 5; i >= 0; i--) { x = (double) i * 0.1; w = (k * x) / sin(x); (void) printf("\tx=%3.3f\t f(x) = % 1.20e\n", x, w); } /* * restore old handler */ (void) ieee_handler("set", "invalid", oldhdl); return 0; }
このプログラムからの出力は次のとおりです。
Evaluating f(x) = (k*x)/sin(x) x=0.500 f(x) = 2.08582964293348816000e+00 x=0.400 f(x) = 2.05434596443822626000e+00 x=0.300 f(x) = 2.03031801709447368000e+00 x=0.200 f(x) = 2.01339581906893761000e+00 x=0.100 f(x) = 2.00333722632695554000e+00 x=0.000 f(x) = 2.00000000000000000000e+00
x = 0 の場合、w を評価すると 0/0 の計算が試みられ、無効な演算例外が発生します。例外ハンドラは、その結果に値 2.0 を代入します。
次の属性については、attributes(5) を参照してください。
|
sigaction(2), signal(3c), sigfpe(3C), abort(3c), fex_set_handling(3M), ieee_flags(3M), attributes(5), siginfo.h(3HEAD), signal.h(3HEAD), ucontext.h(3HEAD)
浮動小数点例外処理ルーチンは、sigfpe(3c)、ieee_handler(3M)、または fex_set_handling(3M) のいずれかを使用してインストールできます。単一のプログラム内で、これらの方法を混在させることはできません。
Intel システムでは、x87 浮動小数点ユニットは、例外のトラップが有効になっている場合 (例外が "unmasked" の場合など) は常にトラップし、それに対応するフラグが立てられます。したがって、ieee_handler() を使用して例外のトラップを有効にすると、ieee_handler() を呼び出したときに例外のフラグがすでに立てられている場合に、後続のトラップが起動されます。このような誤ったトラップを避けるために、プログラムは ieee_handler() を呼び出す前に、トラップを有効にする各例外に対応するフラグをクリアする必要があります。(ieee_flags(3M) 関数は、例外フラグをクリアするための方法の 1 つです。)
多くの場合、ieee_handler() を使用してインストールされたハンドラは、一部の診断情報を出力し、プログラムを終了するだけです。ただし、その代わりにハンドラは、上記の例のように、トラップ命令を実行したスレッドに制御が戻るように正常に返すことができます。現在の SPARC システムでは、トラップが生成された命令の次の命令から実行が続行します。この場合、ハンドラは結果を使用して後続の計算を続行できるように、トラップ命令の宛先に結果を提供する必要があります。Intel システムでは、トラップ命令が SSE 命令の場合に、トラップが生成された命令を再発行することで実行を続行します。この場合、ハンドラは、トラップ命令が再発行されたときに適切な結果を計算できるように、そのトラップ命令のオペランドを変更するか、または代わりにトラップ命令の次の命令から実行を再開できるように、トラップ命令の宛先に適切な結果を提供し、保存された命令ポインタを明示的に指定する必要があります。トラップ命令が x87 命令の場合、トラップが生成された命令の次の浮動小数点命令から動作を続行します。この場合、トラップ命令が完了したように見えるように、ハンドラはトラップ命令の宛先に適切な結果を提供し、必要に応じて保存された x87 レジスタスタックを調整する必要があります。詳細については、Intel IA-32 アーキテクチャーのマニュアルを参照してください。(fex_set_handling(3M) は、浮動小数点例外を処理し、実行を続行するためのずっと単純なインタフェースを備えています。)