1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

C99 の fetestexcept(3) でプログラムを落とさずに浮動小数点例外状態を検知する

Posted at

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 では未実装って警告が出るので、必要なのかよくわかりません。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?