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

6.3 IEEE ルーチン

次のインタフェースは、IEEE 演算を使用するユーザーの役に立ちます。これらのほとんどは、数学ライブラリ libsunmath と、いくつかの .h ファイルに置かれています。

6.3.1 フラグと ieee_flags()

ieee_flags 関数は、例外ステータスフラグの問い合わせやクリアーに使用します。この関数は、Sun コンパイラとともに出荷される libsunmath ライブラリに組み込まれていて、次の作業を行うことができます。

ieee_flags の一般的な呼び出し方法は次のとおりです。


      
flags = ieee_flags( action, mode, in, out )

4 つの引数はすべて文字列です。入力は、actionmode、および in です。出力は、outflags です。ieee_flags は、整数値の関数です。flags は 1 ビットフラグの集合で、ここには有用な情報が返されます。詳細は、ieee_flags(3m) のマニュアルページを参照してください。

パラメータの取り得る値は次のとおりです。

表 6–1 ieee_flags( action, mode, in, out ) の引数の値

引数の 

使用可能な値 

action

getsetclearclearall のいずれか

mode

directionexception

in、out

nearesttozeronegativepositiveextendeddouble singleinexactdivisionunderflowoverflowinvalid allcommon

これらはリテラル文字列であること、出力パラメータ out は最低でも CHARACTER*9 である必要があることに注目してください。inout の取り得る値の意味は、使用時の動作とモードによって異なります。これらの関係を次の表に要約します。

表 6–2 ieee_flags の引数 in と out の意味

inout の値

意味 

nearesttozeronegativepositive

丸めの方向 

extendeddoublesingle

丸めの精度 

inexact、division、underflow、overflow、invalid

例外 

all

5 種類すべての例外 

common

一般的な例外: invalid (無効演算)、division (ゼロ除算)、overflow (オーバーフロー) 

たとえば、フラグが立てられている例外のうちで何がもっとも優先順位が高いかを判別するときは、引数の in に NULL 文字列を渡します。


      CHARACTER *9, out
      ieeer = ieee_flags( 'get', 'exception', '', out )
      PRINT *, out, ' flag raised'

また、overflow 例外フラグが立てられているかどうかを判別するときは、引数の inoverflow を設定します。復帰時に、out が overflow になっていれば、overflow 例外のフラグが立てられているということです。


      ieeer = ieee_flags( 'get', 'exception', 'overflow', out )
      IF ( out.eq. 'overflow') PRINT *, 'overflow flag raised'

例: invalid 例外をクリアーします。


      ieeer = ieee_flags( 'clear', 'exception', 'invalid', out )

例: すべての例外をクリアーします。


      ieeer = ieee_flags( 'clear', 'exception', 'all', out )

例: ゼロに近づけるように丸めます。


      ieeer = ieee_flags( 'set', 'direction', 'tozero', out )

例: 丸めの精度を double に設定します。


      ieeer = ieee_flags( 'set', 'precision', 'double', out )

6.3.1.1 ieee_flags を使用して警告メッセージを抑制する

次の例のように、動作 clear で ieee_flags を呼び出すと、クリアーされてない例外すべてがリセットされます。 プログラムが終了する前にこの呼び出しを置くと、プログラム終了時の浮動小数点例外に関するシステム警告メッセージを抑制します。

例: ieee_flags() を使用して、発生していたすべての例外をクリアーします。


      i = ieee_flags('clear', 'exception', 'all', out )

6.3.1.2 ieee_flags を使用して例外を検出する

次の例は、前の計算によってどの浮動小数点例外フラグが立ったかを決定する方法を示しています。システム include ファイル floatingpoint.h に定義されたビットマスクは、ieee_flags により返された値に適用されます。

この例 DetExcFlg.F では、インクルードファイルは #include 前処理部指令を使用して導入されます。 この指令には、.F 接尾辞のソースファイルを指定しなければいけません。アンダーフローの原因は、最小の倍精度数を 2 で除算しているためです。

例: ieee_flags を使用して例外を検出し、復号化します。


#include "floatingpoint.h"
         CHARACTER*16 out
         DOUBLE PRECISION d_max_subnormal, x
         INTEGER div, flgs, inv, inx, over, under

       x = d_max_subnormal() / 2.0                ! アンダーフローを発生

       flgs=ieee_flags('get','exception','',out)  ! どの例外が発生したのか ?

       inx   = and(rshift(flgs, fp_inexact)  , 1)  ! ieee_flags
       div   = and(rshift(flgs, fp_division) , 1)   ! によって
       under = and(rshift(flgs, fp_underflow), 1)    ! 返される
       over  = and(rshift(flgs, fp_overflow) , 1)     ! 値を
       inv   = and(rshift(flgs, fp_invalid)  , 1)      ! 復号化

       PRINT *, "もっとも優先順位が高い例外:", out
       PRINT *, ' invalid  divide  overflo underflo inexact'
       PRINT '(5i8)', inv, div, over, under, inx
       PRINT *, '(1 = 例外フラグが立っている; 0 = 立っていない)'
       i = ieee_flags('clear', 'exception', 'all', out)    ! すべてクリアー
       END

例: 前出の例 (DetExcFlg.F) をコンパイルして実行します。


demo% f95 DetExcFlg.F
demo% a.out
 もっとも優先順位が高い例外: underflow
  invalid  divide  overflo underflo inexact
       0       0       0       1       1
 (1 = 例外フラグが立っている; 0 = 立っていない)
demo%

6.3.2 IEEE 極値関数

コンパイラは、特別な IEEE 極値を返すために呼び出すことができる関数のセットを提供します。無限大最小の正規化数などの値は、アプリケーションプログラムの中で直接使用できます。

例: 収束のテストはハードウェアによってサポートされる最小数に基づき、次のようになります。


      IF ( delta .LE. r_min_normal() ) RETURN

利用できる値を次の表にリストします。

表 6–3 IEEE の値を使用する関数

IEEE の値 

倍精度 

単精度 

infinity (無限大)

d_infinity()

r_infinity()

quiet NaN (シグナルを発しない非数)

d_quiet_nan()

r_quiet_nan()

signaling NaN (シグナルを発する非数)

d_signaling_nan()

r_signaling_nan()

min normal (最小の正規化数)

d_min_normal()

r_min_normal()

min subnormal (最小の非正規化数)

d_min_subnormal()

r_min_subnormal()

max subnormal (最大の非正規化数)

d_max_subnormal()

r_max_subnormal()

max normal (最大の正規化数)

d_max_normal()

r_max_normal()

2 つの NaN 値 (quietsignaling) は順序がないものなので、IF(X.ne.r_quiet_nan())THEN... のように比較の中で使用してはいけません。値が NaN であるかどうかを判別するときは、ir_isnan(r)id_isnan(d) 関数を使用します。

これらの関数の 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%

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