IEEE 754 では浮動小数点例外をフラグとして保持しておく機構が決められています。
ゼロ割やオーバーフローなどの処理をしてしまったときに、シグナルでプログラムを落とさないとすれば、 NaN や Inf などの特別な値で知らされるわけですが、たとえば NaN が入った数値リストの最大をとれば NaN はなかったことになるべき、などとして、大域的には例外があったことが分からなくなってしまうことがあります。
でも、プロセッサにフラグがあるので、大きなプログラムのどこかでゼロ割が発生したことだけ知らせてくれればよい、という場合には使えます。C99 では fetestexcept(3), feclearexcept(3) を使います。
#include <stdio.h>
#include <string.h>
#include <float.h>
#include <fenv.h>
#pragma STDC FENV_ACCESS ON
void
fecheck(void)
{
int fe;
fe = fetestexcept(FE_ALL_EXCEPT);
printf("FPSTATUS %4s %4s %4s %4s %4s\n",
((fe & FE_INVALID) ? "INVL" : "----"),
((fe & FE_DIVBYZERO) ? "DIVZ" : "----"),
((fe & FE_OVERFLOW) ? "OVFL" : "----"),
((fe & FE_UNDERFLOW) ? "UDFL" : "----"),
((fe & FE_INEXACT) ? "INEX" : "----"));
}
int
main()
{
double a = 1.0;
double b = 2.0;
feclearexcept(FE_ALL_EXCEPT);
fecheck();
printf("1/2=%g\n", a/b);
fecheck();
b = 0.0;
printf("1/0=%g\n", a/b);
fecheck();
b = 3.0;
printf("1/3=%g\n", a/b);
fecheck();
feclearexcept(FE_ALL_EXCEPT);
fecheck();
printf("1/3=%g\n", a/b);
fecheck();
feclearexcept(FE_ALL_EXCEPT);
fecheck();
a = 0.5;
b = DBL_MAX;
printf("0.5/DBL_MAX=%g\n", a/b);
fecheck();
feclearexcept(FE_ALL_EXCEPT);
fecheck();
a = 2.0;
printf("2*DBL_MAX=%g\n", a*b);
fecheck();
feclearexcept(FE_ALL_EXCEPT);
fecheck();
a = 0.0;
b = 0.0;
printf("0/0=%g\n", a/b);
fecheck();
feclearexcept(FE_ALL_EXCEPT);
fecheck();
a = 1.0;
memcpy(&b, "\x7F\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 8);
printf("1*qNaN=%g\n", a*b);
fecheck();
feclearexcept(FE_ALL_EXCEPT);
fecheck();
a = 1.0;
memcpy(&b, "\x7F\xFF\xFF\xFF\xFF\xFF\xF7\xFF", 8);
printf("1*sNaN=%g\n", a*b);
fecheck();
return 0;
}
実行結果
$ gcc -O0 fetestexcept.c -lm
$ ./a.out
FPSTATUS ---- ---- ---- ---- ----
1/2=0.5
FPSTATUS ---- ---- ---- ---- ----
1/0=inf
FPSTATUS ---- DIVZ ---- ---- ----
1/3=0.333333
FPSTATUS ---- DIVZ ---- ---- INEX
FPSTATUS ---- ---- ---- ---- ----
1/3=0.333333
FPSTATUS ---- ---- ---- ---- INEX
FPSTATUS ---- ---- ---- ---- ----
0.5/DBL_MAX=2.78134e-309
FPSTATUS ---- ---- ---- UDFL INEX
FPSTATUS ---- ---- ---- ---- ----
2*DBL_MAX=inf
FPSTATUS ---- ---- OVFL ---- INEX
FPSTATUS ---- ---- ---- ---- ----
0/0=-nan
FPSTATUS INVL ---- ---- ---- ----
FPSTATUS ---- ---- ---- ---- ----
1*qNaN=-nan
FPSTATUS ---- ---- ---- ---- ----
FPSTATUS ---- ---- ---- ---- ----
1*sNaN=-nan
FPSTATUS INVL ---- ---- ---- ----
とりあえず手元の複数環境で動いたので十分ポータブル。もともと C99 なんですからポータブルに決まっているだろう、とは思うのですが必須でもないので。なお、#pragma STDC FENV_ACCESS ON は GCC は実装していないというし icc では未実装って警告が出るので、必要なのかよくわかりません。