MATHERR(3) | Linux Programmer's Manual | MATHERR(3) |
matherr - SVID 数学ライブラリの例外処理
#define _SVID_SOURCE /* feature_test_macros(7) 参照 */ #include <math.h> int matherr(struct exception *exc); extern _LIB_VERSION_TYPE _LIB_VERSION;
-lm でリンクする。
System V Interface Definition (SVID) では、各種の数学関数は数学的な 例外を検出した場合に matherr() を呼ばれる関数を起動すべきである、 と規定されている。この関数は数学関数が返る前に呼び出される。 matherr() が返った後に、システムは数学関数に戻り、 それから呼び出し元に返る。
matherr() の仕組みは glibc によりサポートされているが、 現在は廃止予定の扱いである。 新しくアプリケーションを作成する際には、 math_error(7) と fenv(3) で説明されている手法を使用すべきである。 このマニュアルページでは、古いアプリケーションを保守したり移植する際の 助けとなるよう、 glibc の matherr() の仕組みについて説明する。
matherr() を使用するためには、 プログラマは (どのヘッダーファイルをインクルードするよりも前に) _SVID_SOURCE 機能検査マクロを定義し、値 _SVID_ をグローバル変数 _LIB_VERSION に代入しなければならない。
デフォルト版の matherr() がシステムによって提供されている。 デフォルト版は何も行わず、0 を返す (このことの重要性については 下記を参照)。プログラマが matherr() を定義することで、 デフォルト版を上書きすることができる。 プログラマが定義した関数は例外が発生した際に起動される。 この関数は引き数 1 個で起動され、その引き数は以下に示す exception 構造体へのポインターである。
struct exception {
int type; /* Exception type */
char *name; /* Name of function causing exception */
double arg1; /* 1st argument to function */
double arg2; /* 2nd argument to function */
double retval; /* Function return value */ }
type フィールドは以下の値のいずれかである。
フィールド arg1 と arg2 は関数に渡された引き数である (引き数を一つしか取らない関数の場合は arg2 は不定となる)。
retval フィールドはその数学関数が呼び出し元に返そうとしている返り値 を示す。プログラマが定義した matherr() でこのフィールドを変更する ことで、その数学関数の返り値を変更することができる。
matherr() 関数が 0 を返した場合、 システムは errno を上記の通り設定し、標準エラー出力に エラーメッセージを表示することがある (下記参照)。
matherr() 関数が 0 以外の値を返した場合、 システムは errno を設定せず、エラーメッセージの表示も行わない。
下記の表は、関数と matherr() が呼び出される状況の一覧である。 "Type" 列 は matherr() が呼び出される際に exc->type に 設定される値を示す。 "Result" 列は exc->retval に 設定されるデフォルトの返り値を示す。
"Msg?" 列と "errno" 列は matherr() が 0 を返した場合のデフォルトの 動作を示す。 "Msg?" 列に "y" が入っている場合、システムは標準エラー 出力にエラーメッセージを表示する。
以下の表では、下記の記法と省略形を使用している。
x 関数の最初の引き数 y 関数の二番目の引き数 fin 引き数の値が無限大 neg 引き数が負の値 int 引き数が整数値 o/f 結果のオーバーフロー u/f 結果のアンダーフロー |x| x の絶対値 X_TLOSS <math.h> で定義される定数
Function | Type | Result | Msg? | errno |
acos(|x|>1) | DOMAIN | HUGE | y | EDOM |
asin(|x|>1) | DOMAIN | HUGE | y | EDOM |
atan2(0,0) | DOMAIN | HUGE | y | EDOM |
acosh(x<1) | DOMAIN | NAN | y | EDOM |
atanh(|x|>1) | DOMAIN | NAN | y | EDOM |
atanh(|x|==1) | SING | (x>0.0)? | y | EDOM |
HUGE_VAL : | ||||
-HUGE_VAL | ||||
cosh(fin) o/f | OVERFLOW | HUGE | n | ERANGE |
sinh(fin) o/f | OVERFLOW | (x>0.0) ? | n | ERANGE |
HUGE : -HUGE | ||||
sqrt(x<0) | DOMAIN | 0.0 | y | EDOM |
hypot(fin,fin) o/f | OVERFLOW | HUGE | n | ERANGE |
exp(fin) o/f | OVERFLOW | HUGE | n | ERANGE |
exp(fin) u/f | UNDERFLOW | 0.0 | n | ERANGE |
exp2(fin) o/f | OVERFLOW | HUGE | n | ERANGE |
exp2(fin) u/f | UNDERFLOW | 0.0 | n | ERANGE |
exp10(fin) o/f | OVERFLOW | HUGE | n | ERANGE |
exp10(fin) u/f | UNDERFLOW | 0.0 | n | ERANGE |
j0(|x|>X_TLOSS) | TLOSS | 0.0 | y | ERANGE |
j1(|x|>X_TLOSS) | TLOSS | 0.0 | y | ERANGE |
jn(|x|>X_TLOSS) | TLOSS | 0.0 | y | ERANGE |
y0(x>X_TLOSS) | TLOSS | 0.0 | y | ERANGE |
y1(x>X_TLOSS) | TLOSS | 0.0 | y | ERANGE |
yn(x>X_TLOSS) | TLOSS | 0.0 | y | ERANGE |
y0(0) | DOMAIN | -HUGE | y | EDOM |
y0(x<0) | DOMAIN | -HUGE | y | EDOM |
y1(0) | DOMAIN | -HUGE | y | EDOM |
y1(x<0) | DOMAIN | -HUGE | y | EDOM |
yn(n,0) | DOMAIN | -HUGE | y | EDOM |
yn(x<0) | DOMAIN | -HUGE | y | EDOM |
lgamma(fin) o/f | OVERFLOW | HUGE | n | ERANGE |
lgamma(-int) or | SING | HUGE | y | EDOM |
lgamma(0) | ||||
tgamma(fin) o/f | OVERFLOW | HUGE_VAL | n | ERANGE |
tgamma(-int) | SING | NAN | y | EDOM |
tgamma(0) | SING | copysign( | y | ERANGE |
HUGE_VAL,x) | ||||
log(0) | SING | -HUGE | y | EDOM |
log(x<0) | DOMAIN | -HUGE | y | EDOM |
log2(0) | SING | -HUGE | n | EDOM |
log2(x<0) | DOMAIN | -HUGE | n | EDOM |
log10(0) | SING | -HUGE | y | EDOM |
log10(x<0) | DOMAIN | -HUGE | y | EDOM |
pow(0.0,0.0) | DOMAIN | 0.0 | y | EDOM |
pow(x,y) o/f | OVERFLOW | HUGE | n | ERANGE |
pow(x,y) u/f | UNDERFLOW | 0.0 | n | ERANGE |
pow(NaN,0.0) | DOMAIN | x | n | EDOM |
0**neg | DOMAIN | 0.0 | y | EDOM |
neg**non-int | DOMAIN | 0.0 | y | EDOM |
scalb() o/f | OVERFLOW | (x>0.0) ? | n | ERANGE |
HUGE_VAL : | ||||
-HUGE_VAL | ||||
scalb() u/f | UNDERFLOW | copysign( | n | ERANGE |
0.0,x) | ||||
fmod(x,0) | DOMAIN | x | y | EDOM |
remainder(x,0) | DOMAIN | NAN | y | EDOM |
matherr() 関数はスレッドセーフである。
以下のサンプルプログラムは log(3) を呼び出した際の matherr() の使用法を示したものである。 最初の引き数は log(3) に渡す浮動小数点数である。 省略可能な第二引き数を指定した場合、 _LIB_VERSION に _SVID_ が設定され、 matherr() が呼ばれるようになる。 このコマンドライン引き数で指定した整数は、 matherr() からの返り値として使用される。 省略可能な第三引き数を指定した場合、 matherr() は 数学関数の返り値として代わりに引き数で指定した値を割り当てる。
以下の実行例では、 log(3) に引き数 0.0 が渡しているが、 matherr() は使用しない。
$ ./a.out 0.0 errno: Numerical result out of range x=-inf
以下の実行例では、 matherr() が呼び出され、返り値 0 が返される。
$ ./a.out 0.0 0 matherr SING exception in log() function
args: 0.000000, 0.000000
retval: -340282346638528859811704183484516925440.000000 log: SING error errno: Numerical argument out of domain x=-340282346638528859811704183484516925440.000000
メッセージ "log: SING error" は C ライブラリによって出力されている。
次の実行例では、 matherr() が呼び出され、0 以外の返り値が返される。
$ ./a.out 0.0 1 matherr SING exception in log() function
args: 0.000000, 0.000000
retval: -340282346638528859811704183484516925440.000000 x=-340282346638528859811704183484516925440.000000
この場合は、C ライブラリはメッセージを出力しておらず、 errno は設定されていない。
次の実行例では、 matherr() が呼び出され、 数学関数の返り値が変更され、0 以外の返り値が返されている。
$ ./a.out 0.0 1 12345.0 matherr SING exception in log() function
args: 0.000000, 0.000000
retval: -340282346638528859811704183484516925440.000000 x=12345.000000
#define _SVID_SOURCE #include <errno.h> #include <math.h> #include <stdio.h> #include <stdlib.h> static int matherr_ret = 0; /* Value that matherr()
should return */ static int change_retval = 0; /* Should matherr() change
function's return value? */ static double new_retval; /* New function return value */ int matherr(struct exception *exc) {
fprintf(stderr, "matherr %s exception in %s() function\n",
(exc->type == DOMAIN) ? "DOMAIN" :
(exc->type == OVERFLOW) ? "OVERFLOW" :
(exc->type == UNDERFLOW) ? "UNDERFLOW" :
(exc->type == SING) ? "SING" :
(exc->type == TLOSS) ? "TLOSS" :
(exc->type == PLOSS) ? "PLOSS" : "???",
exc->name);
fprintf(stderr, " args: %f, %f\n",
exc->arg1, exc->arg2);
fprintf(stderr, " retval: %f\n", exc->retval);
if (change_retval)
exc->retval = new_retval;
return matherr_ret; } int main(int argc, char *argv[]) {
double x;
if (argc < 2) {
fprintf(stderr, "Usage: %s <argval>"
" [<matherr-ret> [<new-func-retval>]]\n", argv[0]);
exit(EXIT_FAILURE);
}
if (argc > 2) {
_LIB_VERSION = _SVID_;
matherr_ret = atoi(argv[2]);
}
if (argc > 3) {
change_retval = 1;
new_retval = atof(argv[3]);
}
x = log(atof(argv[1]));
if (errno != 0)
perror("errno");
printf("x=%f\n", x);
exit(EXIT_SUCCESS); }
この man ページは Linux man-pages プロジェクトのリリース 3.79 の一部 である。プロジェクトの説明とバグ報告に関する情報は http://www.kernel.org/doc/man-pages/ に書かれている。
2014-06-13 | Linux |