浮動小数点例外の処理

浮動小数点例外が無効 (1 に設定) になっていると,例外が起こっても割り込みシグナルは生成されません。浮動小数点プロセスは適切な特殊値 (NaN や符号付き無限大など) を返し,プログラムは実行を続けます。浮動小数点例外が有効 (0 に設定) になっていると,例外が起きたときには割り込みシグナル (ソフトウェア割り込み) が生成されます。次表に,浮動小数点例外のシグナルを示します。

パラメタ名 16 進値 説明
FPE$INVALID #81 無効な結果
FPE$DENORMAL #82 規格化されていない演算対象
FPE$ZERODIVIDE #83 ゼロによる除算
FPE$OVERFLOW #84 オーバーフロー
FPE$UNDERFLOW #85 アンダーフロー
FPE$INEXACT #86 不正確な精度

浮動小数点例外割り込みが起こり,プログラマが例外処理ルーチンを用意していなかった場合,実行システムは,コンパイラ・オプション /fpe/math_library によって選択された動作に従って割り込みに対応します。割り込みは例外が有効になっている (0 に設定) ときにのみ起こることに注意してください。

基本システム例外処理を行いたくない場合,独自の割り込み処理ルーチンを作成する必要があります。

割り込み処理ルーチンは ATTRIBUTES C コンパイラ指示文を使わなければならないことに注意してください。

独自のルーチンを作成することの短所は,例外処理ルーチンが,例外を引き起こしたプロセスに返ることができないということです。これは,例外処理ルーチンが呼び出された時点で浮動小数点プロセッサーがエラー状態にあり,ルーチンが返ると,プロセッサーはシステムを終了させたのと同じ状態にあるからです。このため,例外処理ルーチンは別のプログラム単位に飛越すか,(プログラム状態を保存して適切なメッセージを出力した後に) 終了するかのどちらかを行うしかありません。例外処理ルーチンを呼び出したプログラム単位中の別の文に返ることはできません。これは,大域的な GOTO は存在せず,浮動小数点プロセッサー中のステイタス・ワードをリセットすることはできないからです。

例外が起きたことを把握し,またその場合に実行を続ける必要がある場合,割り込みを引き起こさないように例外を無効にした上で,GETSTATUSFPQQ (ia32 のみ) を使って定期的に浮動小数点ステイタス・ワードを調査して,例外が発生したかどうかを調べなくてはなりません。ステイタス・ワード・フラグをクリアするには,CLEARSTATUSFPQQ (ia32 のみ) を呼び出します。

これは明らかにプログラム処理にとって負荷になります。一般論としては,例外が発生したらプログラムを終了させる方がいいでしょう。次に例外処理ルーチンの例を示します。例外処理ルーチン hand_fpe と,これを呼び出すプログラムは,どちらも Visual Fortran サンプルのフォルダ ...\DF98\SAMPLES\TUTORIALSIGTEST.F90 に含まれています。SIGTEST.F90 ファイルの先頭の注釈に,この例のコンパイル方法が記されています。

	! SIGTEST.F90
	! 例外が発生したとき,呼び出される関数として例外処理名を確定します。
	! 例外処理 hand_fpe は以下に添付します。
	USE DFLIB
	INTERFACE
	  FUNCTION hand_fpe (sigid, except)
	    !DEC$ATTRIBUTES C ::hand_fpe
	    INTEGER(4) hand_fpe
	    INTEGER(2) sigid, except
	  END FUNCTION 
	END INTERFACE

	INTEGER(4) iret
	REAL(4) r1, r2

	r1 = 0.0
	iret = SIGNALQQ(SIG$FPE, hand_fpe)
	WRITE(*,*) 'Set exception handler. Return = ', iret
	! ゼロによる除算例外を発生させます。
	r1 = 0.0
	r2 = 3/r1
	END
! 例外処理ルーチン hand_fpe FUNCTION hand_fpe (signum, excnum) !DEC$ ATTRIBUTES C ::hand_fpe USE DFLIB INTEGER(2) signum, excnum WRITE(*,*) 'In signal handler for SIG$FPE' WRITE(*,*) 'signum = ', signum WRITE(*,*) 'exception = ', excnum SELECT CASE(excnum) CASE(FPE$INVALID ) STOP ' Floating point exception:Invalid number' CASE( FPE$DENORMAL ) STOP ' Floating point exception:Denormalized number' CASE( FPE$ZERODIVIDE ) STOP ' Floating point exception:Zero divide' CASE( FPE$OVERFLOW ) STOP ' Floating point exception:Overflow' CASE( FPE$UNDERFLOW ) STOP ' Floating point exception:Underflow' CASE( FPE$INEXACT ) STOP ' Floating point exception:Inexact precision' CASE DEFAULT STOP ' Floating point exception:Non-IEEE type' END SELECT hand_fpe = 1 END