The previous section showed examples of using ieee_handler. In general, when there is a choice between using ieee_handler or sigfpe, the former is recommended.
There are instances, such as trapping integer arithmetic exceptions, when sigfpe is the handler to be used. Example 23 traps on integer division by zero, on SPARC-based systems.
Example 23 Trapping Integer Exceptions/* 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]; }
The following is a simple example of a C driver calling Fortran subroutines. Refer to Oracle Developer Studio 12.5: C User’s Guide and Oracle Developer Studio 12.5: Fortran User’s Guide for more information on working with C and Fortran. The following is the C driver (save it in a file named driver.c):
Example 24 Calling Fortran From C/* * 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; }
Save the Fortran subroutines in a file named 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
Perform the compilation and linking:
cc -c driver.c f95 -c drivee.f demo_one: demo_two: demo_three: f95 -o driver driver.o drivee.o
The output looks like this:
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
The following table shows examples of debugging commands for the SPARC architecture.
|
When displaying floating-point numbers, you should keep in mind that the size of registers is 32 bits, a single precision floating-point number occupies 32 bits (hence it fits in one register), and a double precision floating-point number occupies 64 bits (therefore two registers are used to hold a double precision number). In the hexadecimal representation, 32 bits corresponds to 8 hexadecimal digits. In the following snapshot of FPU registers displayed with adb, the display is organized as follows:
<name of fpu register> <IEEE hex value> <single precision> <double precision>
The third column holds the single precision decimal interpretation of the hexadecimal pattern shown in the second column. The fourth column interprets pairs of registers. For example, the fourth column of the f11 line interprets f10 and f11 as a 64-bit IEEE double precision number.
Because f10 and f11 are used to hold a double precision value, the interpretation (on the f10 line) of the first 32 bits of that value, 7ff00000, as +NaN, is irrelevant. The interpretation of all 64 bits, 7ff00000 00000000, as +Infinity, happens to be the meaningful translation.
The adb command $x, that was used to display the first 16 floating-point data registers, also displayed fsr (the floating-point status register):
$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
The following table shows examples of debugging commands for the x86 architecture:
|
The following examples show two ways to set a breakpoint at the beginning of the code corresponding to a routine myfunction in adb. First you can use:
myfunction:b
Second, you can determine the absolute address that corresponds to the beginning of the piece of code corresponding to myfunction, and then set a break at that absolute address:
myfunction=X 23a8 23a8:b
The main subroutine in a Fortran program compiled with f95 is known as MAIN_ to adb. To set a breakpoint at MAIN_ in adb:
MAIN_:b
When examining the contents of floating-point registers, the hex value shown by the dbx command regs –F is the base-16 representation, not the number's decimal representation. For SPARC-based systems, the adb commands $x and $X display both the hexadecimal representation, and the decimal value. For x86-based systems, the adb command $x displays only the decimal value. For SPARC-based systems, the double precision values show the decimal value next to the odd-numbered register.
Because the operating system disables the floating-point unit until it is first used by a process, you cannot modify the floating-point registers until they have been accessed by the program being debugged.
The corresponding output on x86 looks like the following:
$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