hoge diary - September 28, 2004

[C++] C++ 例外の怪(おまけ)

2004 年 9 月 27 日の laconicDiary で,(re-throwするオブジェクトがないけど) とありますが,まさにそこが「怪」なのです.

引数なしの throw; は例外の再スローを行うことは仕様のようですが,例外ハンドラ以外で再スローすると,なぜ C++ のランタイムライブラリが「ありもしない」例外をハンドルするのか,なぜコンパイルエラーにならないところが不思議です.

ちょっと下の方に書いてあるそれ以前に、10 / 0 (int / int)でどうして「浮動小数点」例外なのかが気になる。という点は私も気になりました.しかしよく考えてみると,浮動小数点で 10.0 / 0.0 を実行すると,値は無限大になるだけで,別に浮動小数点例外が発生するようなことはなかったように記憶しています.試しにやってみることにしました.

#include <iostream>
int main()
{
  std::cout << 10.0 / 0.0 << std::endl;
  return 0;
}

上記のソースコードをコンパイルして実行すると以下の結果が得られます.

% g++ test.cc
test.cc: In function `int main()':
test.cc:4: 警告: division by zero in `1.0e+1 / 0.'
% ./a.out
inf
%

例外は発生していません.一方,整数で 10 / 0 を実行すると... 次の例のように floating point exception が発生します.

#include <iostream>
int main()
{
  std::cout << 10 / 0 << std::endl;
  return 0;
}
% g++ test.cc
test.cc: In function `int main()':
test.cc:4: 警告: division by zero in `10 / 0'
% ./a.out
zsh: 28997 floating point exception  ./a.out
%

当然これは signal(7) の SIGFPE のことです.前者の例から考えると,浮動小数点の 0 除算では「本当の」SIGFPE は発生しないでしょうから,私が推測する理由は次のようなものです.

「UNIX がよく稼働する Sparc では,整数の 0 除算も浮動小数点例外として処理していて,Linux は i386 への実装の際,できるだけ近い実装となるようにした」

(以下 2007 年 2 月 3 日に加筆)

上記の推測が正しいのかどうかは未だに謎なのですが,たまたま見つけた Wikipedia の SIGFPE という項目には以下の記述があります.

SIGFPE is the signal sent to computer programs that perform erroneous arithmetic operations on POSIX compliant platforms.

これを読む限りでは,別に浮動小数点演算に限らず,一般の算術演算に対して SIGFPE が発生するようです.また,SIGFPE の語源についても以下の通りに書いてあります.

Although SIGFPE does not necessarily involve floating-point arithmetic, there is no way to change its name without breaking backward compatibility.

昔に SIGFPE と名付け,今はその名前を変更できないというのは分かりましたが,昔のコンピュータでは SIGFPE が浮動小数点専用だったのかどうたまでは謎のままです.

当時,私がこの文書(加筆前のものです)を書いているときは,不正な浮動小数点演算で SIGFPE を発生させる方法を知りませんでした.が,ちゃんと方法があります.

Tips for the Numerical Computation using GNU/Linux にあるように,feenableexcept() を使用することで,不正な浮動小数点演算時に SIGFPE が発生するようになります.これを踏まえてこの文書の先頭にあるコードを書き直すと次の通りになります.

#include <iostream>
#include <fenv.h>
int main()
{
  feenableexcept(FE_DIVBYZERO);
  std::cout << 10.0 / 0.0 << std::endl;
  return 0;
}

上記のソースコードをコンパイルして実行すると,きちんと浮動小数点演算の 0 除算に対して SIGFPE が発生します.

% g++ test.cc
test.cc: In function `int main()':
test.cc:6: 警告: division by zero in `1.0e+1 / 0.'
% ./a.out
zsh: 7083 floating point exception  ./a.out
%

コメント

名前(何でも可):

テキスト(http:// を含む内容は投稿できません):

トラックバック

トラックバック URI: https://www.pakunet.jp/hoge/trackback/2004092801

トラックバックはありません.


Valid XHTML 1.1! Valid CSS!
© 2004-2009 ぱくちゃん.
Last modified: Fri Nov 02 08:58:03 JST 2007