Oracle® Developer Studio 12.5:数值计算指南

退出打印视图

更新时间: 2016 年 6 月
 
 

5.4 其他

A.4.1 sigfpe:捕获整型异常

上节中显示了使用 ieee_handler 的示例。通常而言,可以选择使用 ieee_handlersigfpe 时,推荐使用前者。


注 -  sigfpe 仅在 Oracle Solaris OS 上可用。

在要使用的处理程序是 sigfpe 时,有诸如捕获整型运算异常等实例。在基于 SPARC 的系统上,示例 23在整数被零除时自陷。

示例 23  捕获整型异常
/* Generate the integer division by zero exception */
#include <signal.h>
#include <siginfo.h>
#include <ucontext.h>
void int_handler(int sig, siginfo_t *sip, ucontext_t *uap);
int main() {
int a, b, c;
/*
* Use sigfpe(3) to establish "int_handler" as the signal handler
* to use on integer division by zero
*/
/*
* Integer division-by-zero aborts unless a signal
* handler for integer division by zero is set up
*/
sigfpe(FPE_INTDIV, int_handler);

a = 4;
b = 0;
c = a / b;
printf("%d / %d = %d\n\n", a, b, c);
return 0;
}
void int_handler(int sig, siginfo_t *sip, ucontext_t *uap) {
printf("Signal %d, code %d, at addr %x\n",
sig, sip->si_code, sip->__data.__fault.__addr);
/*
* automatically for floating-point exceptions but not for
* integer division by zero.
*/
uap->uc_mcontext.gregs[REG_PC] =
uap->uc_mcontext.gregs[REG_nPC];
}

A.4.2 从 C 调用 Fortran

以下为 C 驱动程序调用 Fortran 子例程的简单示例。 有关使用 C 和 Fortran 的更多信息,请参阅Oracle Developer Studio 12.5:C 用户指南Oracle Developer Studio 12.5:Fortran 用户指南。以下为 C 驱动程序(将其保存在名为 driver.c 的文件中):

示例 24  从 C 调用 Fortran
/*
 * a demo program that shows:
 * 1. how to call f95 subroutine from C, passing an array argument
 * 2. how to call single precision f95 function from C
 * 3. how to call double precision f95 function from C
 */

extern int      demo_one_(double *);
extern float    demo_two_(float *);
extern double   demo_three_(double *);

int main()
{
 double array[3][4];
 float f, g;
 double x, y;
 int i, j;

 for (i = 0; i < 3; i++)
  for (j = 0; j < 4; j++)
   array[i][j] = i + 2*j;

 g = 1.5;
 y = g;

 /* pass an array to a fortran function (print the array) */
 demo_one_(&array[0][0]);
 printf(" from the driver\n");
 for (i = 0; i < 3; i++) {
  for (j = 0; j < 4; j++)
   printf("    array[%d][%d] = %e\n",
    i, j, array[i][j]);
  printf("\n");
 }

 /* call a single precision fortran function */
 f = demo_two_(&g);
 printf(
        " f = sin(g) from a single precision fortran function\n");
 printf("    f, g: %8.7e, %8.7e\n", f, g);
 printf("\n");

 /* call a double precision fortran function */
 x = demo_three_(&y);
 printf(
  " x = sin(y) from a double precision fortran function\n");
 printf("    x, y: %18.17e, %18.17e\n", x, y);

 ieee_retrospective_();
 return 0;
}

将 Fortran 子例程保存在名为 drivee.f 的文件中:

subroutine demo_one(array)
double precision array(4,3)
print *, 'from the fortran routine:'
do 10 i =1,4
 do 20 j = 1,3
  print *, '   array[', i, '][', j, '] = ', array(i,j)
20 continue
print *
10 continue
return
end

real function demo_two(number)
real number
demo_two = sin(number)
return
end

double precision function demo_three(number)
double precision number
demo_three = sin(number)
return 
end

执行编译和链接:

cc -c driver.c
f95 -c drivee.f
 demo_one:
 demo_two:
 demo_three:
f95 -o driver driver.o drivee.o

输出结果看起来类似于下文:

 from the fortran routine:
    array[ 1 ][ 1 ] =  0.0E+0
    array[ 1 ][ 2 ] =  1.0
    array[ 1 ][ 3 ] =  2.0

    array[ 2 ][ 1 ] =  2.0
    array[ 2 ][ 2 ] =  3.0
    array[ 2 ][ 3 ] =  4.0

    array[ 3 ][ 1 ] =  4.0
    array[ 3 ][ 2 ] =  5.0
    array[ 3 ][ 3 ] =  6.0

    array[ 4 ][ 1 ] =  6.0
    array[ 4 ][ 2 ] =  7.0
    array[ 4 ][ 3 ] =  8.0

 from the driver
    array[0][0] = 0.000000e+00
    array[0][1] = 2.000000e+00
    array[0][2] = 4.000000e+00
    array[0][3] = 6.000000e+00

    array[1][0] = 1.000000e+00
    array[1][1] = 3.000000e+00
    array[1][2] = 5.000000e+00
    array[1][3] = 7.000000e+00

    array[2][0] = 2.000000e+00
    array[2][1] = 4.000000e+00
    array[2][2] = 6.000000e+00
    array[2][3] = 8.000000e+00

 f = sin(g) from a single precision fortran function
    f, g: 9.9749500e-01, 1.5000000e+00

 x = sin(y) from a double precision fortran function
    x, y: 9.97494986604054446e-01, 1.50000000000000000e+00

A.4.3 有用的调试命令

下表显示了 SPARC 体系结构调试命令的示例。

表 35  一些调试命令 (SPARC)
操作
dbx
adb
在函数上设置断点
stop in myfunct
myfunct:b
在行号上设置断点
stop at 29
n/a
在绝对地址上设置断点
n/a
23a8:b
在相对地址上设置断点
n/a
main+0x40:b
运行直至遇到断点
run
:r
检查源代码
list
<pc,10?ia
检查 fp 寄存器:IEEE 单精度
print $f0
<f0=X
检查 fp 寄存器:(十六进制)等值十进制
print -fx $f0
<f0=f
检查 fp 寄存器:IEEE 双精度
print $f0f1
<f0=X; <f1=X
检查 fp 寄存器:(十六进制)等值十进制
print -flx $f0f1
<f0=F
检查所有 fp 寄存器
regs -F
$x for f0-f15
$X for f16-f31
检查所有寄存器
regs
$r; $x; $X
检查 fp 状态寄存器
print -fllx $fsr
<fsr=X
将单精度 1.0 放在 f0
assign $f0=1.0
3f800000>f0
将双精度 1.0 放在 f0/f1
assign $f0f1=1.0
3ff00000>f0; 0>f1
继续执行
cont
:c
单步
step (or next)
:s
退出调试器
quit
$q

显示浮点数时,应该注意,寄存器的大小为 32 位,单精度浮点数将占据 32 位(因此可以填入一个寄存器),而双精度浮点数占据 64 位(因此使用两个寄存器来存放双精度数)。在十六进制表示形式中,32 位对应于 8 个十六进制位。以下 FPU 寄存器快照使用 adb 显示,其排列如下:

<name of fpu register> <IEEE hex value> <single precision> <double precision>

第三列存放第二列中显示的十六进制模式的单精度十进制解释。第四列解释寄存器对。例如,f11 行的第四列将 f10f11 解释为 64 位 IEEE 双精度数。

由于 f10f11 用于存放双精度值,因此在 f10 行上,将该值的第一个 32 位 7ff00000 解释为 +NaN 是没有任何意义的。将全部 64 位 7ff00000 00000000 解释为 +Infinity 就是有意义的转换。

adb 命令 $x 用于显示前 16 个浮点数据寄存器,同时还显示 fsr(浮点状态寄存器):

$x
fsr    40020
f0 400921fb      +2.1426990e+00 
f1 54442d18      +3.3702806e+12      +3.1415926535897931e+00 
f2        2      +2.8025969e-45 
f3        0      +0.0000000e+00      +4.2439915819305446e-314 
f4 40000000      +2.0000000e+00 
f5        0      +0.0000000e+00      +2.0000000000000000e+00 
f6 3de0b460      +1.0971904e-01 
f7        0      +0.0000000e+00      +1.2154188766544394e-10 
f8 3de0b460      +1.0971904e-01 
f9        0      +0.0000000e+00      +1.2154188766544394e-10 
f10 7ff00000      +NaN 
f11        0      +0.0000000e+00      +Infinity 
f12 ffffffff      -NaN 
f13 ffffffff      -NaN                 -NaN 
f14 ffffffff      -NaN 
f15 ffffffff      -NaN                 -NaN 

下表显示了 x86 体系结构调试命令的示例:

表 36  一些调试命令 (x86)
操作
dbx
adb
在函数上设置断点
stop in myfunct
myfunct:b
在行号上设置断点
stop at 29
n/a
在绝对地址上设置断点
n/a
23a8:b
在相对地址上设置断点
n/a
main+0x40:b
运行直至遇到断点
run
:r
检查源代码
list
<pc,10?ia
检查 fp 寄存器
print $st0
...
print $st7
$x
检查所有寄存器
refs -F
$r
检查 fp 状态寄存器
print -fx $fstat
<fstat=X
继续执行
cont
:c
单步
step (or next)
:s
退出调试器
quit
$q

以下示例显示了两种方法,用于在与 adb 中的例程 myfunction 对应的代码开头设置断点。首先可以使用:

myfunction:b

其次,您可以确定与对应于 myfunction 的代码片段开头部分相对应的绝对地址,然后在该绝对地址上设置断点:

myfunction=X 
   23a8 
23a8:b 

Fortran 程序中使用 f95 编译的主子例程在 adb 中名为 MAIN_。要在 adb 中的 MAIN_ 处设置断点,请键入:

   MAIN_:b

检查浮点寄存器的内容时,dbx 命令 regs –F 显示的十六进制值是 base-16 表示形式,而不是数字的十进制表示形式。对于基于 SPARC 的系统,adb 命令 $x$X 同时显示十六进制表示形式和十进制值。对于基于 x86 的系统,adb 命令 $x 只显示十进制值。对于基于 SPARC 的系统,双精度值显示奇数编号寄存器旁边的十进制值。

由于操作系统在进程首次使用浮点单元之前禁用浮点单元,因此在所调试的程序访问浮点寄存器之前,您无法修改浮点寄存器。

x86 上的对应输出类似于以下内容:

$x
80387 chip is present.
cw      0x137f
sw      0x3920
cssel 0x17  ipoff 0x2d93                datasel 0x1f  dataoff 0x5740
 
 st[0]  +3.24999988079071044921875 e-1            VALID
 st[1]  +5.6539133243479549034419688 e73          EMPTY
 st[2]  +2.0000000000000008881784197              EMPTY
 st[3]  +1.8073218308070440556016047 e-1          EMPTY
 st[4]  +7.9180300235748291015625 e-1             EMPTY
 st[5]  +4.201639036693904927233234 e-13          EMPTY
 st[6]  +4.201639036693904927233234 e-13          EMPTY
 st[7]  +2.7224999213218694649185636              EMPTY

注 -  对于 x86,cw 是控制字,sw 是状态字。