Previous Next Contents Index Doc Set Home


規格への準拠

F


Solaris に対応する Sun WorkShop Compiler 言語製品の中のコンパイラ、ヘッダーファイル、ライブラリは、複数の規格、すなわち System V Interface Definition (SVID) 第 3 版、X/Open、および ANSI C をサポートしています。その結果、数学ライブラリ libm とそれに関連したファイルも、C プログラムが各規格と準拠するように修正されました。この修正の変更点は、主として例外処理関係であるため、ユーザーのプログラムは通常は影響を受けません。

この付録では、次のような構成になっています。

SVID の歴史 422 ページ
IEEE 754 の歴史 424 ページ
SVID の将来の方向 425 ページ
SVID の実装 425 ページ


SVID の歴史

SVID に従った例外処理と IEEE 規格が表わしている立場との相違点を理解するためには、両者が発展してきた状況を検討してみる必要があります。SVID にある考え方の源は、その多くが UNIX の誕生間もない時期、つまりコンピュータ本体上に初めて実装された時期に発しています。このような初期の環境に共通しているのは、有理浮動小数点演算 +-*/ が不可分 (atomic) な機械命令であること、また一方では sqrt、浮動小数点形式での整数値への変換、ならびに基本超越整関数が多数の不可分な機械命令から成るサブルーチンであることです。

これらの環境では浮動小数点例外を多様な方法で処理しますが、一様性を持たせようとすると、不可分 (atomic) な各浮動小数点命令の前後にソフトウェア内で引数と結果をチェックしなければ実現できないと考えられます。しかし、これを行うと性能に及ぼす影響が大きくなりすぎると考えられるので、SVID ではゼロによる除算やオーバーフローなど浮動小数点例外の影響は明示していません。

サブルーチンによって実現される演算は、単一の不可分 (atomic) な浮動小数点命令に比べると速度が劣ります。引数と結果について特別なエラーチェックを行なっても性能にはほとんど影響がないので、SVID ではそのようなチェックを必須にしています。例外が検出されると、デフォルト結果が指定され、不適格なオペランドの場合には errnoEDOM に設定されます。またオーバーフローしたりアンダーフローする結果が出る場合には、errnoERANGE に設定されます。さらに、例外の詳細を含むレコード付きで関数 matherr() が呼び出されます。このことは、UNIX が開発された当初に対象としたマシンにはほとんど負担をかけませんが、基本的な演算 +-*/ における一般的な例外が全く未指定であるため、価値はそれ相応に小さいものとなります。


IEEE 754 の歴史

IEEE 規格は、以前の実装との互換性は目標でなかったと明白に述べています。代わりに、効率とユーザーの要求内容とを念頭に置いて例外処理の機構が開発されました。この機構は、単純な有理演算 ( +-*/ ) と、さらに複雑な剰余、平方根、フォーマット間変換などの演算との両方を通じて一様です。規格では超越関数については規定していませんが、規格の創設者は、準拠システム内の基本超越整関数にも同じ例外処理機構が適用されることを期待していました。

IEEE 例外処理の要素には、あらかじめ要求された場合にのみ、適当なデフォルト結果と計算の中断が含まれます。


SVID の将来の方向

現在の SVID (第 3 版または SVR4) では、将来の発展について一定の方向が明らかにされています。方向の 1 つは IEEE 規格との互換性です。特に、SVID の将来のバージョンにより、大きな有限数用に用意された HUGE への参照が、IEEE システム上での無限大である HUGE_VAL で置換されます。たとえば HUGE_VAL は、浮動小数点オーバーフローの結果として返されます。例外を発生させる入力引数について libm 関数が返す値は、後出の表 F-1 の IEEE 欄に示すものとなります。errno は今後設定される必要がなくなります。


SVID の実装

以下の表に示す libm 関数により、SVID に対応するオペランドチェックまたは結果チェックが行われます。-xlibmil 経由で libm のインライン展開テンプレートを使用する C プログラムから呼び出されたとき、平方根用のハードウェア命令 fsqrt[sd] が関数コールの代わりに使用されるため、sqrt 関数は SVID に準拠しない唯一の関数です。

表 F-1 例外のケースと libm 関数

関数 errno エラーメッセージ SVID X/Open IEEE
acos(|x|>1) EDOM DOMAIN 0.0 0.0 NaN
acosh(x<1) EDOM DOMAIN NaN NaN NaN
asin(|x|>1) EDOM DOMAIN 0.0 0.0 NaN
atan2((+-0,+-0) EDOM DOMAIN 0.0 0.0 +-0.0,+-pi
atanh(|x|>1) EDOM DOMAIN NaN NaN NaN
atanh(+-1) EDOM/ERANGE SING +-HUGE (EDOM) +-HUGE_VAL (ERANGE) +-infinity
cosh overflow ERANGE - HUGE HUGE_VAL infinity
exp overflow ERANGE - HUGE HUGE_VAL infinity
exp underflow ERANGE - 0.0 0.0 0.0
fmod(x,0) EDOM DOMAIN x NaN NaN
gamma(0 or -integer) EDOM SING HUGE HUGE_VAL infinity
gamma overflow ERANGE - HUGE HUGE_VAL infinity
hypot overflow ERANGE - HUGE HUGE_VAL infinity
j0(|x| > X_TLOSS) ERANGE TLOSS 0.0 0.0 correct answer
j1(|x| > X_TLOSS) ERANGE TLOSS 0.0 0.0 correct answer
jn(|x| > X_TLOSS) ERANGE TLOSS 0.0 0.0 correct answer
lgamma(0 or -integer) EDOM SING HUGE HUGE_VAL infinity
lgamma overflow ERANGE - HUGE HUGE_VAL infinity
log(0) EDOM/ERANGE SING -HUGE(EDOM) -HUGE_VAL (ERANGE) infinity
log(x<0) EDOM DOMAIN -HUGE -HUGE_VAL NaN
log10(0) EDOM/ERANGE SING -HUGE(EDOM) -HUGE_VAL (ERANGE) -infinity
log10(x<0) EDOM DOMAIN -HUGE -HUGE_VAL NaN
loglp(-1) EDOM/ERANGE SING -HUGE(EDOM) -HUGE_VAL (ERANGE) -infinity
loglp(x<-1) EDOM DOMAIN NaN NaN NaN
pow(0,0) EDOM DOMAIN 0.0 1.0 (no error) 1.0 (no error)
pow(NaN,0) EDOM DOMAIN NaN NaN 1.0 (no error)
pow(0,neg) EDOM DOMAIN 0.0 -HUGE_VAL +-infinity
pow(neg, non-integer) EDOM DOMAIN 0.0 NaN NaN
pow overflow ERANGE - +-HUGE +-HUGE_VAL +-infinity
pow underflow ERANGE - +-0.0 +-0.0 +-0.0
remainder(x,0) EDOM DOMAIN NaN NaN NaN
scalb overflow ERANGE - +-HUGE_VAL +-HUGE_VAL +-infinity
scalb underflow ERANGE - +-0.0 +-0.0 +-0.0
sinh overflow ERANGE - +-HUGE +-HUGE_VAL +-infinity
sqrt(x<0) EDOM DOMAIN 0.0 NaN NaN
y0(0) EDOM DOMAIN -HUGE -HUGE_VAL -infinity
y0(x<0) EDOM DOMAIN -HUGE -HUGE_VAL NaN
y0(x > X_TLOSS) ERANGE TLOSS 0.0 0.0 correct answer
y1(0) EDOM DOMAIN -HUGE -HUGE_VAL -infinity
y1(x<0) EDOM DOMAIN -HUGE -HUGE_VAL NaN
y1(x > X_TLOSS) ERANGE TLOSS 0.0 0.0 correct answer
yn(n,0) EDOM DOMAIN -HUGE -HUGE_VAL -infinity
yn(n,x<0) EDOM DOMAIN -HUGE -HUGE_VAL NaN
yn(n, x> X_TLOSS) ERANGE TLOSS 0.0 0.0 correct answer

例外のケースと libm 関数についての一般的注意事項

表 F-1 には、各規格の影響を受ける libm 関数がすべてリストされています。値 X_TLOSS は、<values.h> に定義されています。SVID では、<math.h> に対し HUGEMAXFLOAT と定義するように要求しています。これはおよそ 3.4e+38 です。HUGE_VALlibc で無限大と定義されています。errno は、C プログラムおよび C++ プログラムにアクセス可能なグローバル変数です。

<errno.h> では、errno 用として考えられる値を 120 個程度定義しています。数学ライブラリが使用するものが 2 つあります。ドメインエラー用の EDOM と範囲エラー用の ERANGE です。intro(3) と perror(3) を参照してください。

付加スイッチ -libmieee は、指定されると、IEEE 754 に従って値の返却を行います。libm と、libsunmath のデフォルト動作は、SunOS 5.x 上では SVID 準拠になっています。

ユーザーが与えた matherr() により、戻り値が変化することもあります。matherr(3m) を参照してください。ユーザーが与えた matherr() がなければ、libmerrno の設定を行い、メッセージを標準エラー出力ファイルに出力し、426 ページの表 F-1 内で SVID 欄に示されている値を返します。

sqrt() 関数コールを fsqrt[sd] 命令で置換する C プログラムは、IEEE 浮動小数点規格には準拠しますが、System V Interface Definition のエラー処理要求には今後準拠しなくなる可能性があります。

libm についての注意

SVID では、PLOSS (Partial Loss of Significance) と TLOSS (Total Loss of Significance) の 2 つの浮動小数点例外を規定しています。sqrt(-1) と異なり、これらには固有の数学的意味がありません。また、exp (+-10000) と異なり、これらは浮動小数点記憶形式の固有の制限を反映しません。

その代わり PLOSS と TLOSS は、fmod 用の特定アルゴリズム、および明確な境界により不意に正確度がおちる三角関数用の特定アルゴリズムの制限を反映します。

libm のアルゴリズムは、ほとんどの IEEE の実装と同様、そのような不意の低下を被ることがなく、PLOSS のシグナルを出すことはありません。SVID 準拠要件を満たすために、ベッセル関数では、正確な結果は安全に計算できるにもかかわらず、大きい入力引数については TLOSS のシグナルを出します。

sincos、および tan の実装では、無限大での本質的な特異点を、無限引数について EDOM を設定して NaN を返すことにより、他の本質的な特異点と同様に処理します。

同様に SVID では、x/y がオーバーフローすると考えられる場合には、fmod(x,y) は 0 でなくてはならないことを規定しています。しかし、IEEE 剰余関数から派生した fmodlibm の実装では、x/y を明示的に計算せず、必ず厳密な結果をもたらします。


LIA-1 準拠

ここでは、LIA-1 は ISO/IEC 10967-1:1994 Information Technology Language Independent Arithmetic Part 1 のことを差します。言語に依存しない数値演算についての基準です。

C コンパイラ (cc) および FORTRAN 77 コンパイラ (f77) は、Solaris 版 Sun WorkShop Compiler 4.2 に含まれており、以下の点で LIA-1 に準拠しています。行頭のアルファベットは、LIA-1 の第 8 節で使用されているものと対応しています。

a. データ型 (LIA 5.1)

LIA-1 準拠している型には、C の int および FORTRAN の INTEGER があります。この他にも LIA-1 準拠の型はありますが、ここでは取り上げません。その他特定の言語に対する仕様は、言語が LIA-1 に準拠してから決められます。

b. パラメータ (LIA 5.1)

#include <values.h> defines MAXINT
#define TRUE 1
#define FALSE 0
#define BOUNDED TRUE
#define MODULO TRUE
#define MAXINT 2147483647
#define MININT -2147483648
    logical bounded, modulo
    integer maxint, minint
    parameter (bounded = .TRUE.)
    parameter (modulo = .TRUE.)
    parameter (maxint = 2147483647)
    parameter (minint = -2147483648)

d. DIV/REM/MOD (5.1.3)

C の / および % と FORTRAN の / および mod() によって、DIVtI(x,y) と REMtI(x,y) が提供されます。また、modaI(x,y) は、以下のコードによって使用することができます。

int            modaI(int x, int y){
    int            t = x % y;
    if (y < 0 && t > 0)
        t -= y;
    else if (y > 0 && t < 0)
        t += y;
    return t;
}

または

integer function modaI (x, y)
integer x, y, t
t = mod(x,y)
if (y .lt. 0 .and. t .gt. 0) t = t - y
if (y .gt. 0 .and. t .lt. 0) t = t + y
modaI = t
return
end

i. 記数法 (LAI 5.1.3)

LAI-1 の整数演算で認識される記数法を示します。

表 F-2 LAI-1 準拠の記数法

LAI C FORTRAN
(C と異なる場合)
addI(x,y) x+y  
subI(x,y) x-y  
mulI(x,y) x*y  
divtI(x,y) x/y  
remtI(x,y) x%y mod(x,y)
modaI(x,y) x%y mod(x,y)
negI(x) -x  
absI(x) abs(x) #include <stdlib.h> abs(x)
signI(x) #define signI((x) (x>0?1:(x<0?-1:0)) x.eq.y
eqI(x,y) x==y x.eq.y
neqI(x,y) x!=y x.ne.y
lssI(x,y) x<y x.lt.y
leqI(x,y) x<=y x.le.y
gtrI(x,y) x>y x.gt.y
geqI(x,y) x>=y x.ge.y

signI(x) の FORTRAN でのコード例を示します。

integer function signi(x)
integer x, t
if (x .gt. 0) t=1
if (x .lt. 0) t=-1
if (x .eq. 0) t=0
return
end

j. 式の評価

デフォルトでは、最適化が指定されていない場合は、式は C の場合は int、FORTRAN の場合は INTEGER の精度で評価されます。括弧も評価されます。a+b+c または a*b*c などの、括弧で囲まれていない結合式の評価順序は、指定されていません。

k. パラメータの受け取り方

ソースコード中にある上記の定義をインクルードします。

n. 通知

整数の例外は x/0、x%0、mod(x,0) です。デフォルトでは、これらの例外が SIGFPE (SPARC および Intel の場合)または SIGILL (PowerPC の場合) を生成します。シグナルハンドラが指定されていない場合は、プロセスを終了してメモリーダンプを行います。

o. 選択のしくみ

signal(3) および signal(3F) が使用され、ユーザーが SIGFPE に対する例外処理を行うことができるようになります。




Previous Next Contents Index Doc Set Home