Sun Studio 12: Fortran プログラミングガイド

6.3.3 例外ハンドラと ieee_handler()

通常、IEEE 例外について、次のことを知っておく必要があります。

ユーザールーチンへの例外トラップは、システムの浮動小数点例外に対するシグナルの生成から始まります。「浮動小数点例外のシグナル」を表す UNIX の公式名は SIGFPE です。 SPARC プラットフォームは、デフォルトでは、例外が発生しても SIGFPE を生成しません。システムが SIGFPE を生成するためには、まず、例外トラップを有効にしなければいけません。これは通常は、ieee_handler() への呼び出しによって行います。

6.3.3.1 例外ハンドラ関数を設定する

例外ハンドラとして関数を設定するときは、監視する例外や対応動作といっしょに、関数の名前を ieee_handler() に渡します。 ハンドラを一度設定すると、特定の浮動小数点の例外が発生するたびに、SIGFPE シグナルが生成され、指定した関数が呼び出されます。

ieee_handler() を起動する形式を次の表に示します。

表 6–4 ieee_handler( action , exception , handler) の引数

引数 

種類 

可能な値 

action

文字列

getsetclear のいずれか

exception

文字列

invaliddivisionoverflowunderflowinexact のいずれか

handler

関数名 

ユーザーハンドラ関数の名前、または、SIGFPE_DEFAULTSIGFPE_IGNORESIGFPE_ABORT のいずれか

戻り値 

整数

0 =OK

f95 でコンパイルする Fortran 95 ルーチンで ieee_handler() を呼び出す場合は、次の宣言も必要です。

#include 'floatingpoint.h'

特別な引数 SIGFPE_DEFAULTSIGFPE_IGNORE、および SIGFPE_ABORT は、これらのインクルードファイルで定義され、特定の例外に対するプログラムの動作を変更するのに使用できます。

SIGFPE_DEFAULT または SIGFPE_IGNORE

指定した例外が発生しても何も動作しない。 

SIGFPE_ABORT

例外発生時にはプログラムが異常終了し、おそらくダンプファイルを生成する。 

6.3.3.2 ユーザー例外ハンドラ関数を作成する

ユーザーの例外ハンドラが行う動作はユーザーが自由に設定できます。しかし、ルーチンは、次に示す 3 つの引数を取る、整数型関数でなければいけません。

handler_name( sig, sip, uap )

例: 例外ハンドラ関数


      INTEGER FUNCTION hand( sig, sip, uap )
      INTEGER sig, location
      STRUCTURE /fault/
           INTEGER address
           INTEGER trapno
      END STRUCTURE
      STRUCTURE /siginfo/
           INTEGER si_signo
           INTEGER si_code
           INTEGER si_errno
           RECORD /fault/ fault
      END STRUCTURE
      RECORD /siginfo/ sip
      location = sip.fault.address
      ... ユーザーの行う処理 ...
      END

64 ビット SPARC アーキテクチャーで実行するには、STRUCTURE 内のすべての INTEGER 宣言を INTEGER*8 で置き換えて、この例を変更する必要があります。

ieee_handler() によって有効にされるハンドラルーチンが例で示すように Fortran で書かれている場合、ハンドラルーチンは 1 番目の引数 (sig) に対しては、一切の参照を行なってはいけません。1 番目の引数は値でルーチンに渡され、loc(sig) としてのみ参照できます。この値はシグナル番号です。この値はシグナル番号です。

ハンドラを使用して例外を検出する

次の例では、浮動小数点の例外を検出するためのハンドラルーチンを作成する方法を示します。

例: 例外を検出して、異常終了します。


demo% cat DetExcHan.f
      EXTERNAL myhandler
      REAL :: r = 14.2 , s = 0.0
      i = ieee_handler ('set', 'division', myhandler )
      t = r/s
      END

      INTEGER FUNCTION myhandler(sig,code,context)
      INTEGER sig, code, context(5)
      CALL abort()
      END
demo% f95 DetExcHan.f
demo% a.out
異常終了
demo%

SIGFPE は、浮動小数点演算の例外が発生すると必ず生成されます。SIGFPE が検出されると、制御が myhandler 関数に渡され、即座に異常終了します。-g を付けてコンパイルし、dbx を使用すると、例外箇所を突き止めることができます。

ハンドラを使用して例外箇所を突き止める

例: 例外箇所を突き止め (アドレスを出力して)、異常終了します。


demo% cat LocExcHan.F
#include "floatingpoint.h"
      EXTERNAL Exhandler
      INTEGER Exhandler, i, ieee_handler
      REAL:: r = 14.2 ,  s = 0.0 , t
C  ゼロ除算の検出
      i = ieee_handler( 'set', 'division', Exhandler )
      t = r/s
      END

      INTEGER FUNCTION Exhandler( sig, sip, uap)
      INTEGER sig
      STRUCTURE /fault/
                  INTEGER address
      END STRUCTURE
      STRUCTURE /siginfo/
                  INTEGER si_signo
                  INTEGER si_code
                  INTEGER si_errno
                  RECORD /fault/ fault
      END STRUCTURE
      RECORD /siginfo/ sip
      WRITE (*,10)  sip.si_signo, sip.si_code, sip.fault.address
10        FORMAT(' シグナル ',i4,' コード ',i4,' 16 進アドレス ', Z8 )
      Exhandler=1
      CALL abort()
      END
demo%  f95 -g LocExcHan.F
demo%  a.out
シグナル 8 コード 3 16 進アドレス 11230
異常終了
demo%

64 ビット SPARC 環境では、各 STRUCTURE 内の INTEGER 宣言を INTEGER*8 で置き換えて、書式中の i4i8 で置き換えます。この例では、f95 コンパイラへの拡張により、VAX Fortran の STRUCTURE 文を受け付けられるようにしています。

ほとんどの場合、例外の実際のアドレスを知るということは、dbx だけに意味があります。


demo% dbx a.out
(dbx) stopi at 0x11230     ブレークポイントをアドレスに設定する
(2) stopi at &MAIN+0x68
(dbx) run               プログラムを実行する
実行中: a.out
(プロセス id 18803)
MAIN で 0x11230 で停止しました
MAIN+0x68:      fdivs   %f3, %f2, %f2
(dbx) where           例外が発生した行番号を表示する
=>[1] MAIN()、"LocExcHan.F" の 7 行目
(dbx) list 7          ソースコード行を表示する
    7         t = r/s
(dbx) cont            ブレークポイント後、継続してハンドラルーチンに入る
シグナル 8 コード 3 16 進アドレス 11230
異常終了:
_kill 0xef6e18a4 でシグナル ABRT (異常終了) が呼び出されました
_kill+0x8:      bgeu    _kill+0x30
現関数: exhandler
   24         CALL abort()
(dbx) quit
demo%

もちろん、エラーの原因となるソース行を決定するためのより簡単な方法があります。しかし、この例は、例外処理の基本を示すのが目的です。