マニュアルページ ieee_handler.3m
名前
ieee_handler - IEEE 例外トラップハンドラ関数
形式
cc [ flag ... ] file ... -lsunmath -lm [ library ... ]
#include <sunmath.h>
long ieee_handler(const char *action, const char *exception,
sigfpe_handler_type hdl);
機能説明
この関数は、C プログラム中で ANSI/IEEE 規格 754-1985 算術 機
構 を利用するための例外処理機能を提供します。第 1 引数および
第 2 引数は文字列を指すポインタです。処理効率を考慮し、無 効
な引数または無効な組み合せの引数を指定した場合の結果は未定義
です。
引数 action には例外発生時のアクショ ン と し て、 ``get''、
``set''、 ``clear'' の いずれかの文字列を指定できます。引数
exception には、例外として次のいずれかの文字列を指定で き ま
す。
``inexact''
``division'' ... ゼロ除算例外
``underflow''
``overflow''
``invalid''
``all'' ... 上記 5 つの例外すべて
``common'' ... 無効な演算、オーバーフロー、ゼロ除算
の 3 つの例外
注意 : ``all'' と ``common'' には、``set'' または ``clear''
を指定した場合にだけ意味をもちます。
hdl は、 シ グ ナ ル 処 理 ルー チ ン の ア ド レ ス で す。
<floatingpoint.h> は、 sigfpe_handler_type を定義します。
引数 action に ``get'' を指定すると、 exception 用の現在有効
なハンドラルーチンの位置を long 型に変換した値が返されます。
``set'' を指定すると、 hdl が指すルーチンがハンドラルーチ ン
と し て 設定され、 exception に対するトラップが有効になりま
す。ただし、 hdl の値が SIGFPE_DEFAULT または SIGFPE_IGNORE
の場合は、 exception 発生時のトラップを無効にします。 hdl の
値が SIGFPE_ABORT の場合、 exception に対するトラップが発 生
すると abort(3) を用いてコアダンプが出力されます。
``clear'' は exception のトラップを無効にし、 exception 用の
ハンドラルーチンを SIGFPE_DEFAULT に設定します。``clear'' と
``all'' を指定すると、5 種類のすべての例外トラップが無効にな
り、 5 種 類 す べ て の 例 外 に 対 するハンドラルーチンを
SIGFPE_DEFAULT に設定します。
IEEE 関連の SIGFPE コードを ieee_handler によって取得する に
は、次の手順が必要です。
1) ieee_handler によりハンドラを設定する。
2) 意図する IEEE 例外を発生するような浮動小数点処理を実 行
する。
ieee_handler() は、 IEEE のトラップ処理に影響を与えるよう な
浮 動 小 数 点 ハー ドウェア・モードビットの調整も行います。
``clear''、 ``set'' と SIGFPE_DEFAULT、 ``set'' と
SIGFPE_IGNORE のいずれかの組み合わせでは、ハードウェアトラッ
プは無効になります。``set'' とそれ以外の組み合わせでは、ハー
ドウェアトラップが有効になります。
SIGFPE シグナルは、 sigaction(2) 、 ieee_handler(3M) また は
fex_set_handling(3M) を用いて処理することができます。
ただし混乱を避けるため、1 つのプログラムの中では、これらのイ
ン タフェースのいずれか 1 つを使って SIGFPE シグナルを処理し
てください。
診断
``set'' を指定した場合、 ieee_handler() の戻り値は正常終了時
には 0、指定したアクションが利用できない場合 (たとえば、ハー
ドウェアでサポートされていない場合) は 1 になります。``get''
を指定した場合の戻り値は、現在のハンドラのアドレスを long に
型変換した値です。
使用例
ゼロをゼロで割ったことで例外が発生した場合に無効なシグナルを
トラップし、デフォルトの出力 (非数) をユーザーが指定する値に
変更する例を示します。以下は、ユーザーが指定したシグナルハン
ドラです。(SPARC システムのみに有効)
/*
* ユーザー例外処理ルーチンの例。無効なシグナルをトラップした後、
* 例外が 0/0 によるものかどうかを確認する。そうであれば、結果を
* zero_over_zero_value (ユーザー指定値) に設定する。
*/
#include <sunmath.h>
#include <siginfo.h>
#include <ucontext.h>
extern double zero_over_zero_value;
void zero_over_zero_handler(int sig, siginfo_t *sip, ucontext_t *uap) {
fpregset_t *uc = &uap->uc_mcontext.fpregs; /* fpregset_t の構造については <sys/reg.h> を参照*/
const int fopshift = 5, frdshift = 25, frs1shift = 14, frs2shift = 0;
int i, j, fop, frd, frs1, frs2;
int *con = (int *) &zero_over_zero_value;
/*
* レジスタ rd、rs1、rs2、opf の検索
*/
fop = ((uc->fpu_q->FQu.fpq.fpq_instr)>>fopshift) &0x1ff;
frd = ((uc->fpu_q->FQu.fpq.fpq_instr)>>frdshift) &0x1f;
frs1= ((uc->fpu_q->FQu.fpq.fpq_instr)>>frs1shift)&0x1f;
frs2= ((uc->fpu_q->FQu.fpq.fpq_instr)>>frs2shift )&0x1f;
/*
* rs1 と rs2 がともにゼロ (0/0 型の例外) かどうかの確認
*/
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];
switch (fop) {
case 0x4e: /* fdivd */
if((i|j) == 0) { /* 0/0、rd を zero_over_zero_value に設定*/
uc->fpu_fr.fpu_regs[frd] = con[0];
uc->fpu_fr.fpu_regs[frd+1] = con[1];
}
break;
}
}
このルーチンは次のように設定できます。
#include <stdio.h>
#include <stdlib.h>
#include <sunmath.h>
#include <siginfo.h>
#include <ucontext.h>
extern void zero_over_zero_handler(int, siginfo_t *, ucontext_t *);
double zero_over_zero_value;
void
main(void) {
extern double sin(double);
double x, w;
int i, k;
sigfpe_handler_type hdl, old_handler1;
/*
* 現在の "invalid" に対するハンドラを保存
*/
old_handler1 = (sigfpe_handler_type) ieee_handler("get", "invalid", (sigfpe_handler_type)0);
/*
* 新しく "invalid" に対するハンドラを設定
*/
hdl = (sigfpe_handler_type) zero_over_zero_handler;
(void) ieee_handler("set", "invalid", hdl);
/*
* k=2, x=0.5, 0.4, ..., 0.1, 0.0 で (k*x)/sin(x) を計算
*/
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);
}
/*
* 古い "invalid" に対するハンドラを回復
*/
(void) ieee_handler("set", "invalid", old_handler1);
exit(0);
/* NOTREACHED */
}
出力は次のようになります。
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 のとき、f(x) = 0/0 となり、"invalid" 例外が発生してい る
点 に注意してください。この例では、0/0 の値が 2.0 に設定され
ています。
属性
次の属性については、 attributes(5) を参照してください。
_________________________________
| 属性タイプ | 属性値 |
|_____________________|__________|
| 使用条件 | SPROlang|
| インタフェース安定性| 開発中 |
| MT レベル | MT-安全 |
|_____________________|__________|
関連項目
sigaction(2), signal(3C), sigfpe(3), abort(3C),
fex_set_handling(3M), ieee_flags(3M), attributes(5), sig-
info(5), signal(5), ucontext(5)
注意事項
Intel システムでは、浮動小数点ハードウェアは、例外のトラップ
が有効なとき (つまり、例外がマスクされていないとき) は常にト
ラップし、それに対応するフラグを立て ま す。 し た がっ て、
ieee_handler() 経 由 で 例 外 の ト ラッ プを有効にすると、
ieee_handler() を呼び出したときに例外のフラグがすでに立っ て
いた場合、後続のトラップを誘発します。このような偽のトラップ
を避けるために、プログラムは、 ieee_handler() を呼び出 す 前
に、トラップが有効にされる例外ごとに、それに対応するフラグを
クリアしなければなりません。 ieee_flags(3M) 関数は、例外フラ
グをクリアする 1 つの方法です。