以下接口可帮助用户使用 IEEE 运算,并在手册页中进行说明。这些接口多数都在数学库 libsunmath 和几个 .h 文件中。
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 ) |
四个参数中的每一个都是字符串。输入为 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 的可能值的含义取决于与其一起使用的 action 和 mode。下表对此进行了概括:
表 6–2 ieee_flags in、 out 参数的含义
in 和 out 的值 |
所指 |
---|---|
nearest, tozero, negative, positive |
舍入方向 |
extended, double, single |
舍入精度 |
inexact, division, underflow, overflow, invalid |
异常 |
all |
全部五种异常 |
common |
常见异常:无效、除法、上溢 |
例如,要确定引起了标志的具有最高优先级的异常,请将输入参数 in 作为空字符串传递:
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 ) |
使用清除 action 调用 ieee_flags(如下例所示)可以重置任何未清除的异常。在程序退出之前进行该调用,可禁止系统在程序终止时产生浮点异常警告消息。
示例:用 ieee_flags() 清除所有产生的异常:
i = ieee_flags(’clear’, ’exception’, ’all’, out ) |
以下示例演示如何确定早期计算引起的浮点异常。会将系统 include 文件 floatingpoint.h 中定义的位屏蔽应用于 ieee_flags 的返回值。
在以下示例(即 DetExcFlg.F)中,include 文件是使用 #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 ! Cause underflow flgs=ieee_flags(’get’,’exception’,’’,out) ! Which are raised? inx = and(rshift(flgs, fp_inexact) , 1) ! Decode div = and(rshift(flgs, fp_division) , 1) ! the value under = and(rshift(flgs, fp_underflow), 1) ! returned over = and(rshift(flgs, fp_overflow) , 1) ! by inv = and(rshift(flgs, fp_invalid) , 1) ! ieee_flags PRINT *, "Highest priority exception is: ", out PRINT *, ’ invalid divide overflo underflo inexact’ PRINT ’(5i8)’, inv, div, over, under, inx PRINT *, ’(1 = exception is raised; 0 = it is not)’ i = ieee_flags(’clear’, ’exception’, ’all’, out) ! Clear all END |
示例:编译并运行上述示例 (DetExcFlg.F):
demo% f95 DetExcFlg.F demo% a.out Highest priority exception is: underflow invalid divide overflo underflo inexact 0 0 0 1 1 (1 = exception is raised; 0 = it is not) demo% |
编译器提供了一个函数集,可以调用其中的函数来返回特殊的 IEEE 极值。这些值,如 infinity 或 minimum normal,可以直接在应用程序中使用。
示例:基于硬件支持的最小数值的收敛测试如下所示:
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() |
两个 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 异常,通常需要关注以下问题:
用户例程的异常捕获以系统产生浮点异常信号开始。signal: floating-point exception的 UNIX 标准名称是 SIGFPE。出现异常时,SPARC 平台上的缺省情况是不产生 SIGFPE。要使系统产生 SIGFPE,必须先启用异常捕获,这通常通过对 ieee_handler() 的调用来完成。
要将函数作为异常处理程序建立,请将函数名称与要监视的异常的名称和要采取的操作一起传递给 ieee_handler()。一旦建立了处理程序,无论何时出现特定的浮点异常和调用指定的函数,都会产生 SIGFPE 信号。
ieee_handler() 的调用形式如下表所示:
表 6–4 ieee_handler (action , exception , handler) 的参数
参数 |
类型 |
可能值 |
---|---|---|
action |
character |
get、set 或 clear |
exception |
character |
invalid、division、overflow、underflow 或 inexact |
handler |
函数名 |
用户处理函数的名称或 SIGFPE_DEFAULT、SIGFPE_IGNORE 或 SIGFPE_ABORT |
返回值 |
integer |
0 =OK |
用 f95 编译的、调用 ieee_handler() 的 Fortran 95 例程还应该声明:
#include ’floatingpoint.h’
特殊参数 SIGFPE_DEFAULT、SIGFPE_IGNORE 和 SIGFPE_ABORT 定义在这些包含文件中,可用于更改与特定异常相应的程序行为:
SIGFPE_DEFAULT 或 SIGFPE_IGNORE |
出现指定异常时不采取任何操作。 |
SIGFPE_ABORT |
程序在异常时中止(可能会使用转储文件)。 |
异常处理程序采取的操作由您决定。但是,此例程必须是整型函数,且具有下面指定的三个参数:
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 ... actions you take ... END |
此示例需要修改才能运行在 64 位 SPARC 体系结构上,方法是使用 INTEGER*8 替换每个 STRUCTURE 中的所有 INTEGER 声明。
如果由 ieee_handler() 启用的处理程序例程与此示例中一样,是用 Fortran 编写的,则此例程不能对其第一个参数 (sig) 进行任何引用。该第一个参数按值传递给此例程,并且只能作为 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 Detect division by zero 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(’Signal ’,i4,’ code ’,i4,’ at hex address ’, Z8 ) Exhandler=1 CALL abort() END demo% f95 -g LocExcHan.F demo% a.out Signal 8 code 3 at hex address 11230 Abort demo% |
在 64 位 SPARC 环境中,请用 INTEGER*8 替换每个 STRUCTURE 中的 INTEGER 声明,用 i8 替换 i4 格式。(注意,该例接受 VAX Fortran STRUCTURE 语句,依靠的是 f95 编译器的扩展。)
大多数情况下,知道异常的实际地址并无太大用处,但对于 dbx 除外:
demo% dbx a.out (dbx) stopi at 0x11230 Set breakpoint at address (2) stopi at &MAIN+0x68 (dbx) run Run program Running: a.out (process id 18803) stopped in MAIN at 0x11230 MAIN+0x68: fdivs %f3, %f2, %f2 (dbx) where Shows the line number of the exception =>[1] MAIN(), line 7 in "LocExcHan.F" (dbx) list 7 Displays the source code line 7 t = r/s (dbx) cont Continue after breakpoint, enter handler routine Signal 8 code 3 at hex address 11230 abort: called signal ABRT (Abort) in _kill at 0xef6e18a4 _kill+0x8: bgeu _kill+0x30 Current function is exhandler 24 CALL abort() (dbx) quit demo% |
当然,还有更容易的方法来确定引起错误的源码行。但是,本例确实足以展示异常处理的基本内容。