Oracle® Solaris Studio 12.4:数值计算指南

退出打印视图

更新时间: 2015 年 1 月
 
 

标准符合性

Oracle Solaris Studio 编译器产品与 Solaris 10 操作环境中的头文件和库一起,可支持多种标准,包括 System V 接口定义第 3 版 (System V Interface Definition, SVID)、X/Open、ANSI C (C90)、POSIX.1-2001 (SUSv3) 和 ISO C (C99)。 (有关更完整的讨论,请参见“标准(5)”。)其中某些标准允许在特定层面上有不同的实现。在某些情况下,这些标准的规范相互冲突。对于数学库,差异和冲突主要与特殊情况和异常有关。本附录记录了 libm 中的函数在这些情形下的行为,并讨论了在哪些条件下可以预期 C 程序的行为符合各个标准。本附录的最后一节记录了 Sun Studio C 和 Fortran 语言产品遵循 LIA-1 的情况。

E.1 libm 特殊情况

Table E–1 列出了所有这些情况,在这些情况中,以上涉及的两个或更多标准为 libm 中的函数指定了相互冲突的行为。在 C 程序上将观察到什么样的行为取决于在编译和链接程序时使用的编译器标志。可能的行为包括引发浮点异常、使用有关所发生特殊情况的信息以及返回的值调用用户提供的函数 matherr(请参见 matherr(3M))、在标准错误文件中输出消息以及设置全局变量 errno(请参见 intro(2) 和 perror(3C))。

Table E–1 中的第一列定义了特殊情况。第二列显示 errno 在设置时将设置的值。errno 的可能值定义在 <errno.h> 中;数学库中仅使用两个值,对于域错误为 EDOM,对于范围错误为 ERANGE。第二列同时显示 EDOMERANGE 时,errno 设置的值由相关标准确定,这些标准将在下面介绍,并显示在第四列或第五列中。第三列显示在输出的任意错误消息中指示的错误代码。第四、五、六列显示按照不同标准的定义在名义上返回的函数值。在一些情况下,用户提供的 matherr 例程可以覆盖这些值并提供其他返回值。

对这些特殊情况的特定响应由在链接程序时指定的编译器标志确定,如下所示。如果指定了 –xlibmieee–xc99=lib,则出现Table E–1 中的任何特殊情况时,将引发任意适当的浮点异常,并且返回该表的第六列中列出的函数值。

如果未使用 –xlibmieee–xc99=lib,则行为取决于在链接程序时指定的语言符合性标志。

指定 -Xa 标志可选择 X/Open 符合性。出现表中的任何特殊情况时,将引发任何适当的浮点异常,设置 errno,并且返回该表的第五列中列出的函数值。如果提供了用户定义的 matherr 例程,则不定义行为。请注意,在未提供任何其他语言符合性标志时,–Xa 是缺省值。

指定 -Xc 标志可选择严格 C90 符合性。出现特殊情况时,将引发任何适当的浮点异常,设置 errno,返回该表的第五列中列出的函数值。这种情况下不调用 matherr

最后,指定 –Xs–Xt 标志可选择 SVID 符合性。出现特殊情况时,将引发任意适当的浮点异常,调用 matherr,如果 matherr 返回零,则设置 errno 并返回错误消息。除非由 matherr 覆盖,否则将返回该表的第四列中列出的函数值。

有关 –xc99–Xa–Xc–Xs–Xt 标志的更多信息,请参见 cc(1) 手册页和Oracle Solaris Studio 12.4:C 用户指南

表 E-1  特殊情况和 libm 函数
函数
errno
错误消息
SVID
X/Open,C90
IEEE,C99,SUSv3
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
+/‐HUGE1 (EDOM)
+/‐HUGE_VAL2 (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_TLOSS3<|x|< inf)
ERANGE
TLOSS
0.0
0.0
computed answer
j1(X_TLOSS<|x|< inf)
ERANGE
TLOSS
0.0
0.0
computed answer
jn(n,X_TLOSS<|x|< inf)
ERANGE
TLOSS
0.0
0.0
computed answer
ldexp overflow
ERANGE
-
+-infinity
+/-infinity
+/-infinity
ldexp underflow
ERANGE
-
+/-0.0
+/-0.0
+/-0.0
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
log1p(‐1)
EDOM/ERANGE
SING
‐HUGE (EDOM)
‐HUGE_VAL (ERANGE)
‐infinity
log1p(x<‐1)
EDOM
DOMAIN
NaN
NaN
NaN
logb(0)
EDOM
-
-HUGE_VAL
-HUGE_VAL
-infinity
nextafter overflow
ERANGE
-
+-HUGE_VAL
+/-HUGE_VAL
+/-infinity
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,x<0)
EDOM
DOMAIN
0.0
‐HUGE_VAL
+/‐infinity
pow(x<0, 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) or remainder(inf,y)
EDOM
DOMAIN
NaN
NaN
NaN
scalb overflow
ERANGE
+‐HUGE_VAL
+/‐HUGE_VAL
+/‐infinity
scalb underflow
ERANGE
+/‐0.0
+/‐0.0
+/‐0.0
scalb(0,+inf) or scalb(inf,-inf)
EDOM/ERANGE
NaN
(ERANGE)
NaN
(EDOM)
NaN
scalb(|x|>0,+inf)
ERANGE
+‐infinity
+/‐infinity
(no error)
+/‐infinity
(no error)
scalb(|x|<inf, ‐inf)
ERANGE
+/‐0.0
+/‐0.0
(no error)
+/‐0.0
(no error)
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_TLOSS<x<inf)
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_TLOSS<x<inf)
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_TLOSS<x< inf)
ERANGE
TLOSS
0.0
0.0
correct answer

注:

  1. HUGE 定义在 <math.h> 中。SVID 要求 HUGE 等于 MAXFLOAT,这大约为 3.4e+38。

  2. HUGE_VAL 定义在 <iso/math_iso.h> 中,后者包括在 <math.h> 中。HUGE_VAL 求值为无穷。

  3. X_TLOSS 定义在 <values.h> 中。

E.1.1 影响标准符合性的其他编译器标志

上面列出的编译器标志直接选择在处理Table E–1 中列出的特殊情况时应遵守哪几个标准。其他编译器标志可以间接影响是否会在程序上观察到上述行为。

首先,–xlibmil–xlibmopt 标志替换 libm 中一些函数的更快的实现。这些更快的实现不符合 SVID、X/Open 或 C90。它们不设置 errno,也不调用 matherr。但是,它们会相应引发浮点异常,并提供 IEEE 754 和/或 C99 规定的结果。类似的注释适用于 –xvector 标志,因为它会导致编译器将对标准数学函数的调用转换为对向量数学函数的调用。

其次,–xbuiltin 标志允许编译器将 <math.h> 中定义的标准数学函数作为内部函数处理,并且替换内联代码以实现更好的性能。替换代码可能不符合 SVID、X/Open、C90 或 C99。它不需要设置 errno、调用 matherr 或引发浮点异常。

第三,在定义 C 预处理程序标记 __MATHERR_ERRNO_DONTCARE 的情况下,将编译 <math.h> 中的多条 #pragma 指令。这些指令告知编译器假设标准数学函数没有任何副作用。根据这一假设,编译器可以重新排序对数学函数的调用,并且引用全局数据(例如 errno)或可能由用户提供的 matherr 例程修改的数据,这样可以改变上面介绍的预期行为。例如,请考虑代码片段:

#include <errno.h>
#include <math.h>

...
errno = 0;
x = acos(2.0);
if (errno) {
    printf(“error\n”);
}

如果在定义了 __MATHERR_ERRNO_DONTCARE 的情况下编译此代码,则编译器可能会假设 errno 未由对 acos 的调用修改,并相应地转换代码,完全删除对 printf 的调用。

请注意,–fast 宏标记包括标志 –xbuiltin–xlibmil–xlibmopt–D__MATHERR_ERRNO_DONTCARE

最后,由于 libm 中的所有数学函数会根据需要引发浮点异常,运行允许捕获这些异常的程序时,通常会导致超出以上所列标准的规定的行为。因此,–ftrap 编译器标志还会影响标准符合性。

E.1.2 关于 C99 符合性的补充说明

C99 指定了两种可能的方法,实现会按照这些方法来处理诸如Table E–1 中的特殊情况。实现可通过定义标识符 math_errhandling 来计算具有值 MATH_ERRNO (1)、MATH_ERREXCEPT (2) 或者这两个值的按位“或”的整数表达式,以此指示支持两种方法中的哪一个。(这些值定义在 <math.h> 中。)如果表达式 (math_errhandling & MATH_ERRNO) 非零,则实现可通过将 errno 设置为 EDOM 来处理函数参数位于其数学域之外的情况,通过将 errno 设置为 ERANGE 来准确地处理函数的结果值可能下溢、溢出或等于无穷的情况。如果表达式 (math_errhandling & MATH_ERREXCEPT) 非零,则实现可通过引发无效操作异常来处理函数参数位于其数学域之外的情况,通过引发下溢、溢出或被零除来准确地处理函数的结果值可能下溢、溢出或等于无穷的情况。

在 Oracle Solaris 上,<math.h>math_errhandling 定义为 MATH_ERREXCEPT。虽然Table E–1 中列出的函数可以为特殊情况执行此处列出之外的其他操作,所有 libm 函数(包括 floatlong double 函数、复数函数和 C99 指定的其他函数)通过引发浮点异常来响应特殊情况。这是所有 C99 函数统一支持处理特殊情况的唯一方法。

最后请注意,有三个函数,不论是 C99 还是 SUSv3,都需要不同于 Oracle Solaris 缺省值的行为。下表汇总了这些区别。表中仅列出了各函数的 double 版本,但是这些区别也适用于 floatlong double 版本。在各种情况下,使用 -xc99=lib 链接程序时将遵循 SUSv3 规范,否则将遵循 Solaris 缺省值。

表 E-2  Solaris 和 C99/SUSv3 的区别
函数
Solaris 行为
C99/SUSv3 行为
pow
pow(1.0, +/-inf) 返回 NaN
pow(-1.0, +/-inf) 返回 NaN
pow(1.0, NaN) 返回 NaN
pow(1.0, +/-inf) 返回 1
pow(-1.0, +/-inf) 返回 1
pow(1.0, NaN) 返回 1
logb
logb(subnormal) 返回 Emin
logb(x) = ilogb(x)x 为次正规数时)
ilogb
ilogb(+/-0), ilogb(+/-inf),
ilogb(NaN) 不引发异常
ilogb(+/-0), ilogb(+/-inf),
ilogb(NaN) 引发无效操作

E.2 LIA-1 符合性

在本节中,LIA-1 指的是 ISO/IEC 10967-1:1994 Information Technology - Language Independent Arithmetic - Part 1: Integer and floating-point arithmetic(ISO/IEC 10967-1:1994 信心技术 - 语言独立技术 - 第 1 部分:整数和浮点计算)。

Sun Studio 编译器发行版中包含的 C 和 Fortran 95 编译器(ccf95),在以下几个方面符合 LIA-1(段落字母编号与 LIA-1 第 8 节对应):

E.2.1 a.类型 (LIA 5.1):

LIA-1 符合标准的类型为 C int 和 Fortran INTEGER。也可以遵从其他类型,但此处未指明。特定语言的更多规范尚待审理语言标准组织将语言结合到 LIA-1。

E.2.2 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)

E.2.3 d.DIV/REM/MOD (LIA 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

E.2.4 i.表示法 (LIA 5.1.3):

下表显示了可以实现 LIA 整数运算的表示法。

表 E-3  LIA-1 符合性-表示法
LIA
C
Fortran(如有不同)
addI(x,y)
x+y
n/a
subI(x,y)
x‐y
n/a
mulI(x,y)
x*y
n/a
divtI(x,y)
x/y
n/a
remtI(x,y)
x%y
mod(x,y)
modaI(x,y)
see above
n/a
negI(x)
‐x
n/a
absI(x)
#include <stdlib.h>
abs(x)
abs(x)
signI(x)
#define signI(x) (x > 0
? 1 : (x < 0 ? ‐1 : 0))
请参见下文
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

E.2.5 j.表达式求值:

缺省情况下,在未指定任何优化时,表达式按照 int (C) 或 INTEGER (Fortran) 精度求值。建议使用括号。未使用括号的联合表达式的求值顺序未指定,例如 a + b + c 或 a * b * c。

E.2.6 k.获取参数的方法:

b.参数 (LIA 5.1): 中的定义包含在您的源代码中。

E.2.7 n.通知:

整数异常为 x/0 和 x%0 或 mod(x,0)。缺省情况下,这些异常生成 SIGFPE。没有为 SIGFPE 指定信号处理程序时,进程将会终止并且转储内存。

E.2.8 o.选择机制:

signal(3) 或 signal(3F) 可用于启用 SIGFPE 的用户异常处理。