次のインタフェースは、IEEE 演算を使用するユーザーの役に立ちます。これらのほとんどは、数学ライブラリ libsunmath と、いくつかの .h ファイルに置かれています。
ieee_flags(3m) - 丸めの方向と丸めの精度を制御し、例外ステータスの問い合わせと例外ステータスのクリアーを行います。
ieee_functions(3m) - 個々の IEEE 関数の名前と目的をリストします。
その他の libm 関数 (本節で説明)
ieee_retrospective
nonstandard_arithmetic
standard_arithmetic
SPARC プロセッサは、さまざまな側面においてソフトウェアとハードウェアサポートを組み合わせることによって、IEEE 基準に準拠しています。
最新の SPARC プロセッサには浮動小数点ユニットが含まれており、整数の乗算と除算の命令とハードウェアによる平方根演算機能を備えています。
コンパイルしたコードが実行時の浮動小数点ハードウェアに適切に一致したとき、最高のパフォーマンスが得られます。コンパイラの -xtarget= オプションは、実行時ハードウェアの指定を許可します。たとえば、-xtarget=ultra は、UltraSPARC プロセッサ上で最高のパフォーマンスを得られるオブジェクトコードを生成することをコンパイラに伝えます。
fpversion ユーティリティーは、浮動小数点のどのハードウェアがインストールされているかを表示し、指定すべき適切な -xtarget 値を示します。このユーティリティーは、すべての Sun SPARC アーキテクチャー上で動作します。詳細は、fpversion(1) のマニュアルページ、『Fortran ユーザーズガイド』、『 数値計算ガイド』を参照してください。
ieee_flags 関数は、例外ステータスフラグの問い合わせやクリアーに使用します。この関数は、Sun コンパイラとともに出荷される libsunmath ライブラリに組み込まれていて、次の作業を行うことができます。
丸めの方向と丸めの精度の制御
例外ステータスフラグの検査
例外ステータスフラグのクリアー
ieee_flags の一般的な呼び出し方法は次のとおりです。
flags = ieee_flags( action, mode, in, out ) |
4 つの引数はすべて文字列です。入力は、action、mode、および in です。出力は、out と flags です。ieee_flags は、整数値の関数です。flags は 1 ビットフラグの集合で、ここには有用な情報が返されます。詳細は、ieee_flags(3m) のマニュアルページを参照してください。
表 6–1 ieee_flags( action, mode, in, out ) の引数の値|
引数の |
使用可能な値 |
|---|---|
|
action |
get、set、clear、clearall のいずれか |
|
mode |
direction、exception |
|
in、out |
nearest、tozero、negative、positive、extended、double single、inexact、division、underflow、overflow、invalid all、common |
これらはリテラル文字列であること、出力パラメータ out は最低でも CHARACTER*9 である必要があることに注目してください。in と out の取り得る値の意味は、使用時の動作とモードによって異なります。これらの関係を次の表に要約します。
表 6–2 ieee_flags の引数 in と out の意味|
in と out の値 |
意味 |
|---|---|
|
nearest、tozero、negative、positive |
丸めの方向 |
|
extended、double、single |
丸めの精度 |
|
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 例外フラグが立てられているかどうかを判別するときは、引数の in に overflow を設定します。復帰時に、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 ) |
次の例のように、動作 clear で ieee_flags を呼び出すと、クリアーされてない例外すべてがリセットされます。 プログラムが終了する前にこの呼び出しを置くと、プログラム終了時の浮動小数点例外に関するシステム警告メッセージを抑制します。
例: ieee_flags() を使用して、発生していたすべての例外をクリアーします。
i = ieee_flags('clear', 'exception', 'all', out )
|
次の例は、前の計算によってどの浮動小数点例外フラグが立ったかを決定する方法を示しています。システム 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%
|
コンパイラは、特別な 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 値 (quiet と signaling) は順序がないものなので、IF(X.ne.r_quiet_nan())THEN... のように比較の中で使用してはいけません。値が NaN であるかどうかを判別するときは、ir_isnan(r) か id_isnan(d) 関数を使用します。
これらの関数の Fortran 名は、次のマニュアルページにリストされています。
libm_double(3f)
libm_single(3f)
ieee_functions(3m)
また、次も参照してください。
ieee_values(3m)
floatingpoint.h ヘッダーファイルと floatingpoint(3f)
通常、IEEE 例外について、次のことを知っておく必要があります。
例外が発生するときのシステムの動作。
例外ハンドラとして使用できる関数を作成する方法。
例外の発生場所を調べる方法。
ユーザールーチンへの例外トラップは、システムの浮動小数点例外に対するシグナルの生成から始まります。「浮動小数点例外のシグナル」を表す UNIX の公式名は SIGFPE です。 SPARC プラットフォームは、デフォルトでは、例外が発生しても SIGFPE を生成しません。システムが SIGFPE を生成するためには、まず、例外トラップを有効にしなければいけません。これは通常は、ieee_handler() への呼び出しによって行います。
例外ハンドラとして関数を設定するときは、監視する例外や対応動作といっしょに、関数の名前を ieee_handler() に渡します。 ハンドラを一度設定すると、特定の浮動小数点の例外が発生するたびに、SIGFPE シグナルが生成され、指定した関数が呼び出されます。
ieee_handler() を起動する形式を次の表に示します。
表 6–4 ieee_handler( action , exception , handler) の引数|
引数 |
種類 |
可能な値 |
|---|---|---|
|
action |
文字列 |
get、set、clear のいずれか |
|
exception |
文字列 |
invalid、division、overflow、underflow、inexact のいずれか |
|
handler |
関数名 |
ユーザーハンドラ関数の名前、または、SIGFPE_DEFAULT、SIGFPE_IGNORE、SIGFPE_ABORT のいずれか |
|
戻り値 |
整数 |
0 =OK |
f95 でコンパイルする Fortran 95 ルーチンで ieee_handler() を呼び出す場合は、次の宣言も必要です。
#include 'floatingpoint.h'
特別な引数 SIGFPE_DEFAULT、SIGFPE_IGNORE、および SIGFPE_ABORT は、これらのインクルードファイルで定義され、特定の例外に対するプログラムの動作を変更するのに使用できます。
|
SIGFPE_DEFAULT または SIGFPE_IGNORE |
指定した例外が発生しても何も動作しない。 |
|
SIGFPE_ABORT |
例外発生時にはプログラムが異常終了し、おそらくダンプファイルを生成する。 |
ユーザーの例外ハンドラが行う動作はユーザーが自由に設定できます。しかし、ルーチンは、次に示す 3 つの引数を取る、整数型関数でなければいけません。
handler_name( sig, sip, uap )
handler_name は整数関数の名前です。
sig は整数です。
sip は構造体 siginfo を持つレコードです。
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) としてのみ参照できます。この値はシグナル番号です。この値はシグナル番号です。
次の例では、浮動小数点の例外を検出するためのハンドラルーチンを作成する方法を示します。
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 で置き換えて、書式中の i4 を i8 で置き換えます。この例では、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%
|
もちろん、エラーの原因となるソース行を決定するためのより簡単な方法があります。しかし、この例は、例外処理の基本を示すのが目的です。