618 lines
16 KiB
Plaintext
618 lines
16 KiB
Plaintext
#include <stdio.h>
|
|
#include <math.h>
|
|
#include <signal.h>
|
|
<P>
|
|
void fpenab_(void);
|
|
void hanfpe();
|
|
<P>
|
|
main() {
|
|
float a,b,c,d;
|
|
int ia,ib,ic;
|
|
int i;
|
|
|
|
signal(8,hanfpe);
|
|
fpenab_();
|
|
|
|
a=1.; b=0.;
|
|
c=a/b;
|
|
d=(float)sqrt((double)-a);
|
|
printf("Float: a=%f b=%f a/b=%f d=sqrt(-a)=%f\n",a,b,c,d);
|
|
ia=1; ib=0;
|
|
ic=ia/ib;
|
|
printf("Integer: ia=%d ib=%d ia/ib=%d\n",ia,ib,ic);
|
|
}
|
|
<P>
|
|
void hanfpe(void) {
|
|
printf ("We have a floating point exception...\n");
|
|
printf ("Bye Bye...\n");
|
|
exit(1);
|
|
}
|
|
---------------------------------------------------------------------------
|
|
/*---------------------------------------------------------------------------+
|
|
| Floating-point environment <fenv.c> |
|
|
| |
|
|
| |
|
|
| Copyright (C) 1996, 1997 |
|
|
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
|
|
| Australia. E-mail billm@suburbia.net |
|
|
| |
|
|
| See the files "README" and "COPYING" for further copyright and warranty |
|
|
| information. |
|
|
| |
|
|
+---------------------------------------------------------------------------*/
|
|
|
|
/*---------------------------------------------------------------------------+
|
|
| History: |
|
|
| 29 Feb 1996: First version, based upon the definitions in |
|
|
| WG14/N513 X3J11/95-114 (Draft 12/21/95) |
|
|
| 14 May 1996: Let the sticky bit functions preform in a similar manner |
|
|
| even if there are unmasked exceptions. |
|
|
+---------------------------------------------------------------------------*/
|
|
<P>
|
|
#define _LOOSE_KERNEL_NAMES
|
|
<P>
|
|
#include <stdio.h>
|
|
<P>
|
|
#define __SIGFPE_INTERNAL
|
|
#define NON_PORTABLE_FENV
|
|
/* #include <fenv.h> */
|
|
/*---------------------------------------------------------------------------+
|
|
| Floating-point environment <fenv.h> |
|
|
| |
|
|
| |
|
|
| Copyright (C) 1996, 1997 |
|
|
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
|
|
| Australia. E-mail billm@suburbia.net |
|
|
| |
|
|
| See the files "README" and "COPYING" for further copyright and warranty |
|
|
| information. |
|
|
| |
|
|
+---------------------------------------------------------------------------*/
|
|
|
|
/*---------------------------------------------------------------------------+
|
|
| History: |
|
|
| 10 Sep 1993: First version, based upon early NCEG docs. |
|
|
| 29 Feb 1996: Second version, minor modifications to satisfy |
|
|
| WG14/N513 X3J11/95-114 (Draft 12/21/95) |
|
|
+---------------------------------------------------------------------------*/
|
|
<P>
|
|
<P>
|
|
#ifndef _FENV_H
|
|
#define _FENV_H
|
|
<P>
|
|
#define FE_INEXACT 0x20
|
|
#define FE_DIVBYZERO 0x04
|
|
#define FE_UNDERFLOW 0x10
|
|
#define FE_OVERFLOW 0x08
|
|
#define FE_INVALID 0x01
|
|
#define FE_ALL_EXCEPT ( FE_INEXACT | FE_DIVBYZERO | FE_UNDERFLOW | FE_OVERFLOW | FE_INVALID )
|
|
<P>
|
|
#define FE_TONEAREST 0x000
|
|
#define FE_UPWARD 0x800
|
|
#define FE_DOWNWARD 0x400
|
|
#define FE_TOWARDZERO 0xc00
|
|
<P>
|
|
#define FE_DFL_ENV (&__default_fp_env)
|
|
<P>
|
|
/* fenv_t: a type for representing the entire floating-point environment. */
|
|
/* fenv_t is not defined in detail by NCEG.
|
|
For Linux, we use the FPU environment: */
|
|
struct __fenv {
|
|
unsigned long control;
|
|
unsigned long status;
|
|
unsigned long tags;
|
|
unsigned long ip_offset;
|
|
unsigned long opcode;
|
|
unsigned long data_offset;
|
|
unsigned long op_sel;
|
|
};
|
|
typedef struct __fenv fenv_t;
|
|
<P>
|
|
extern const fenv_t __default_fp_env;
|
|
<P>
|
|
/* fexcept_t: a type for representing the floating-point exception flags
|
|
collectively, including any status associated with the flags. */
|
|
/* fexcept_t is not defined in detail by NCEG.
|
|
For Linux, it might be something like this: */
|
|
struct __fexcept {
|
|
int excepts;
|
|
void *code_address;
|
|
void *data_address;
|
|
};
|
|
typedef struct __fexcept fexcept_t;
|
|
<P>
|
|
__BEGIN_DECLS
|
|
void feclearexcept(int excepts);
|
|
void feraiseexcept(int excepts);
|
|
int fetestexcept(int excepts);
|
|
<P>
|
|
void fegetexceptflag(fexcept_t *flagp, int excepts);
|
|
void fesetexceptflag(const fexcept_t *flagp, int excepts);
|
|
<P>
|
|
int fegetround(void);
|
|
int fesetround(int round);
|
|
<P>
|
|
void fegetenv(fenv_t *envp);
|
|
int feholdexcept(fenv_t *envp);
|
|
void fesetenv(const fenv_t *envp);
|
|
void feupdateenv(const fenv_t *envp);
|
|
__END_DECLS
|
|
<P>
|
|
#ifdef NON_PORTABLE_FENV
|
|
<P>
|
|
#define FE_DENORM_OP 0x02 /* 80x87, Not mentioned by NCEG */
|
|
#define FE_STACK_FLT 0x40 /* 80x87, Not mentioned by NCEG */
|
|
#undef FE_ALL_EXCEPT
|
|
#define FE_ALL_EXCEPT ( FE_INEXACT | FE_DIVBYZERO | FE_UNDERFLOW | FE_OVERFLOW | FE_INVALID | FE_DENORM_OP )
|
|
<P>
|
|
<P>
|
|
#define FE_FLTPREC 0x000
|
|
#define FE_INVALIDPREC 0x100
|
|
#define FE_DBLPREC 0x200
|
|
#define FE_LDBLPREC 0x300
|
|
<P>
|
|
/*
|
|
These are non-standard functions.
|
|
They will probably be removed in a future release.
|
|
DO NOT USE if you require portability.
|
|
*/
|
|
#include <asm/sigcontext.h>
|
|
#if defined(__cplusplus)
|
|
/* My version of g++ doesn't like Linus' definition of asmlinkage */
|
|
#define _PERSONALITY_H
|
|
#endif
|
|
#include <linux/sched.h>
|
|
<P>
|
|
/*
|
|
Change this to "#if 1" if you get warning and/or error messages
|
|
from gcc about sigcontext_struct.
|
|
The name of the structure has changed in <asm/sigcontext.h>, grrrh!.
|
|
*/
|
|
#if 0 /* %% Change this line as needed. %% */
|
|
#define sigcontext_struct sigcontext
|
|
#endif
|
|
<P>
|
|
__BEGIN_DECLS
|
|
<P>
|
|
typedef int (*ftrap_t)(struct sigcontext_struct *sig,
|
|
struct i387_hard_struct *fpu );
|
|
<P>
|
|
int fegetprecision(void);
|
|
int fesetprecision(int prec);
|
|
<P>
|
|
int fegetvector(ftrap_t *trap, int except);
|
|
int fesetvector(ftrap_t *trap, int except);
|
|
int fegettraps(void);
|
|
int fedisabletraps(int excepts);
|
|
int feenabletraps(int excepts);
|
|
<P>
|
|
void report_fpu_exception_stats(FILE *f);
|
|
<P>
|
|
<P>
|
|
#ifdef __SIGFPE_INTERNAL
|
|
struct fptraps {
|
|
ftrap_t invalid, stack_flt, denorm_op,
|
|
divbyzero, overflow, underflow, inexact;
|
|
int trap_invalid, trap_stack_flt;
|
|
};
|
|
<P>
|
|
extern struct fptraps __trap_vectors;
|
|
extern void __set_sticky(int bits);
|
|
#endif /* __SIGFPE_INTERNAL */
|
|
<P>
|
|
__END_DECLS
|
|
<P>
|
|
#endif /* NON_PORTABLE_FENV */
|
|
<P>
|
|
#endif /* _FENV_H */
|
|
<P>
|
|
<P>
|
|
/* A place to keep the unmasked "sticky" bits. */
|
|
static int sticky;
|
|
<P>
|
|
/*
|
|
Clears the supported exceptions represented by the argument.
|
|
*/
|
|
void feclearexcept(int excepts)
|
|
{
|
|
fenv_t env;
|
|
asm volatile ("fnstenv %0"::"m" (env));
|
|
excepts &= FE_ALL_EXCEPT;
|
|
sticky &= ~excepts;
|
|
env.status &= ~excepts;
|
|
if ( !(env.status & FE_ALL_EXCEPT) )
|
|
env.status &= 0xff00; /* No bits set, so clear summary as well. */
|
|
else if ( !(env.status & FE_INVALID) )
|
|
env.status &= ~FE_STACK_FLT; /* Invalid bit not set, so clear this. */
|
|
asm volatile ("fldenv %0":"=m" (env));
|
|
}
|
|
<P>
|
|
<P>
|
|
/*
|
|
Raises the supported exceptions represented by the argument.
|
|
*/
|
|
void feraiseexcept(int excepts)
|
|
{
|
|
fenv_t env;
|
|
asm volatile ("fnstenv %0"::"m" (env));
|
|
env.status |= excepts & FE_ALL_EXCEPT;
|
|
sticky |= excepts & ~env.control & FE_ALL_EXCEPT;
|
|
asm volatile ("fldenv %0":"=m" (env));
|
|
}
|
|
<P>
|
|
<P>
|
|
/*
|
|
Store the exception flags indicated by excepts in *flagp.
|
|
*/
|
|
void fegetexceptflag(fexcept_t *flagp, int excepts)
|
|
{
|
|
}
|
|
<P>
|
|
<P>
|
|
/*
|
|
Set the exception flags indicated by excepts from *flagp.
|
|
*/
|
|
void fesetexceptflag(const fexcept_t *flagp, int excepts)
|
|
{
|
|
}
|
|
<P>
|
|
<P>
|
|
void __set_sticky(int bits)
|
|
{
|
|
sticky |= bits & FE_ALL_EXCEPT;
|
|
}
|
|
<P>
|
|
<P>
|
|
/*
|
|
Returns the bitwise OR of the exception macros corresponding
|
|
to the currently set exceptions included in excepts.
|
|
*/
|
|
int fetestexcept(int excepts)
|
|
{
|
|
unsigned sw;
|
|
asm volatile ("fnstsw %0":"=m" (sw));
|
|
return (sw | sticky) & excepts & FE_ALL_EXCEPT;
|
|
}
|
|
<P>
|
|
<P>
|
|
/*
|
|
Return the rounding direction macro representing the current
|
|
rounding direction.
|
|
*/
|
|
int fegetround(void)
|
|
{
|
|
unsigned short cw;
|
|
asm volatile ("fnstcw %0":"=m" (cw));
|
|
return cw & FE_TOWARDZERO;
|
|
}
|
|
<P>
|
|
<P>
|
|
/*
|
|
Set rounding direction to round if it is a valid
|
|
rounding direction macro.
|
|
Returns 1 if rounding direction set, 0 otherwise.
|
|
*/
|
|
int fesetround(int round)
|
|
{
|
|
unsigned short cw;
|
|
<P>
|
|
asm volatile ("fnstcw %0":"=m" (cw));
|
|
if ( ! (round & ~FE_TOWARDZERO) )
|
|
{
|
|
/* round matches a rounding direction macro. */
|
|
cw = (cw & ~FE_TOWARDZERO) | (round & FE_TOWARDZERO);
|
|
asm volatile ("fldcw %0"::"m" (cw));
|
|
return 1;
|
|
}
|
|
else
|
|
return 0; /* Invalid argument. */
|
|
}
|
|
<P>
|
|
<P>
|
|
/*
|
|
Return the current floating point environment
|
|
in *envp.
|
|
*/
|
|
void fegetenv(fenv_t *envp)
|
|
{
|
|
asm volatile ("fnstenv %0":"=m" (*envp));
|
|
}
|
|
<P>
|
|
<P>
|
|
/*
|
|
Save current environment in *envp,
|
|
clear exception flags, and
|
|
install "non-stop" mode.
|
|
*/
|
|
int feholdexcept(fenv_t *envp)
|
|
{
|
|
fenv_t env;
|
|
asm volatile ("fnstenv %0":"=m" (*envp)); /* Masks exceptions. */
|
|
asm volatile ("fnstenv %0":"=m" (env));
|
|
env.status &= 0xff00; /* Clear exception flags. */
|
|
asm volatile ("fldenv %0"::"m" (env));
|
|
return 1; /* Always succeeds. */
|
|
}
|
|
<P>
|
|
<P>
|
|
/*
|
|
Set floating point environment.
|
|
Don't raise exceptions.
|
|
*/
|
|
void fesetenv(const fenv_t *envp)
|
|
{
|
|
asm volatile ("fldenv %0"::"m" (*envp));
|
|
}
|
|
<P>
|
|
<P>
|
|
/*
|
|
Install new environment with old exceptions.
|
|
*/
|
|
void feupdateenv(const fenv_t *envp)
|
|
{
|
|
fenv_t env = *envp;
|
|
unsigned short sw;
|
|
asm volatile ("fnstsw %0":"=m" (sw));
|
|
env.status = sw;
|
|
asm volatile ("fldenv %0"::"m" (*envp));
|
|
}
|
|
<P>
|
|
<P>
|
|
/*
|
|
Return the precision macro representing the current
|
|
floating point precision.
|
|
*/
|
|
int fegetprecision(void)
|
|
{
|
|
unsigned short cw;
|
|
asm volatile ("fnstcw %0":"=m" (cw));
|
|
return cw & FE_LDBLPREC;
|
|
}
|
|
<P>
|
|
<P>
|
|
/*
|
|
Set the precision to prec if it is a valid
|
|
floating point precision macro.
|
|
Returns 1 if precision set, 0 otherwise.
|
|
*/
|
|
int fesetprecision(int prec)
|
|
{
|
|
unsigned short cw;
|
|
asm volatile ("fnstcw %0":"=m" (cw));
|
|
if ( !(prec & ~FE_LDBLPREC) && (prec != FE_INVALIDPREC) )
|
|
{
|
|
cw = (cw & ~FE_LDBLPREC) | (prec & FE_LDBLPREC);
|
|
asm volatile ("fldcw %0"::"m" (cw));
|
|
return 1;
|
|
}
|
|
else
|
|
return 0;
|
|
}
|
|
<P>
|
|
<P>
|
|
int
|
|
__sigfpe_abort(char *s, void *p)
|
|
{
|
|
char line[81];
|
|
FILE *cmd;
|
|
|
|
cmd = fopen("/proc/self/cmdline", "r");
|
|
if ( cmd )
|
|
fscanf(cmd, "%80s", line);
|
|
else
|
|
line[0] = 0;
|
|
line[80] = 0;
|
|
fprintf(stderr, "FATAL: %s %s at %p\n",
|
|
line, s, p);
|
|
exit(1);
|
|
return 0; /* Keep gcc happy. */
|
|
}
|
|
<P>
|
|
<P>
|
|
int default_stack_flt_handler(struct sigcontext_struct *sig,
|
|
struct i387_hard_struct *fpu)
|
|
{
|
|
return __sigfpe_abort("got FPU stack fault", (void *)fpu->fip);
|
|
}
|
|
<P>
|
|
<P>
|
|
int default_invalid_handler(struct sigcontext_struct *sig,
|
|
struct i387_hard_struct *fpu)
|
|
{
|
|
return __sigfpe_abort("attempted invalid FPU operation", (void *)fpu->fip);
|
|
}
|
|
<P>
|
|
<P>
|
|
int default_divbyzero_handler(struct sigcontext_struct *sig,
|
|
struct i387_hard_struct *fpu)
|
|
{
|
|
return __sigfpe_abort("attempted FPU divide-by-zero", (void *)fpu->fip);
|
|
}
|
|
<P>
|
|
<P>
|
|
struct fptraps __trap_vectors =
|
|
{ default_invalid_handler, default_stack_flt_handler, 0,
|
|
default_divbyzero_handler, 0, 0, 0,
|
|
0, 1
|
|
};
|
|
<P>
|
|
/*
|
|
Return the exception trap macros representing the current
|
|
enabled traps.
|
|
*/
|
|
int fegettraps(void)
|
|
{
|
|
unsigned short cw;
|
|
asm volatile ("fnstcw %0":"=m" (cw));
|
|
cw = ~cw & FE_ALL_EXCEPT;
|
|
if ( cw & FE_INVALID )
|
|
{
|
|
if ( ! __trap_vectors.trap_invalid )
|
|
cw &= ~FE_INVALID;
|
|
if ( __trap_vectors.trap_stack_flt )
|
|
cw |= FE_STACK_FLT;
|
|
}
|
|
return cw;
|
|
}
|
|
<P>
|
|
<P>
|
|
/*
|
|
Enable exception traps for the exceptions specified by traps.
|
|
*/
|
|
int feenabletraps(int traps)
|
|
{
|
|
unsigned short cw;
|
|
<P>
|
|
asm volatile ("fnstcw %0":"=m" (cw));
|
|
if ( ! (traps & ~(FE_ALL_EXCEPT | FE_STACK_FLT)) )
|
|
{
|
|
if ( traps & (FE_INVALID | FE_STACK_FLT) )
|
|
{
|
|
if ( traps & FE_INVALID )
|
|
__trap_vectors.trap_invalid = 1;
|
|
if ( traps & FE_STACK_FLT )
|
|
__trap_vectors.trap_stack_flt = 1;
|
|
traps |= FE_INVALID;
|
|
}
|
|
cw &= ~traps;
|
|
asm volatile ("fldcw %0"::"m" (cw));
|
|
return 1;
|
|
}
|
|
else
|
|
return 0;
|
|
}
|
|
<P>
|
|
<P>
|
|
/*
|
|
Disable exception traps for the exceptions specified by traps.
|
|
*/
|
|
int fedisabletraps(int traps)
|
|
{
|
|
unsigned short cw;
|
|
<P>
|
|
asm volatile ("fnstcw %0":"=m" (cw));
|
|
if ( ! (traps & ~(FE_ALL_EXCEPT | FE_STACK_FLT)) )
|
|
{
|
|
if ( traps & (FE_INVALID | FE_STACK_FLT) )
|
|
{
|
|
if ( traps & FE_INVALID )
|
|
__trap_vectors.trap_invalid = 0;
|
|
if ( traps & FE_STACK_FLT )
|
|
__trap_vectors.trap_stack_flt = 0;
|
|
if ( !(__trap_vectors.trap_invalid | __trap_vectors.trap_stack_flt) )
|
|
traps |= FE_INVALID;
|
|
}
|
|
cw |= (traps & FE_ALL_EXCEPT);
|
|
asm volatile ("fldcw %0"::"m" (cw));
|
|
return 1;
|
|
}
|
|
else
|
|
return 0;
|
|
}
|
|
<P>
|
|
<P>
|
|
/*
|
|
Get the trap vector for the specified exception.
|
|
*/
|
|
int fegetvector(ftrap_t *vector, int excep)
|
|
{
|
|
switch ( excep )
|
|
{
|
|
case FE_STACK_FLT:
|
|
*vector = __trap_vectors.stack_flt;
|
|
break;
|
|
case FE_INVALID:
|
|
*vector = __trap_vectors.invalid;
|
|
break;
|
|
case FE_DIVBYZERO:
|
|
*vector = __trap_vectors.divbyzero;
|
|
break;
|
|
case FE_OVERFLOW:
|
|
*vector = __trap_vectors.overflow;
|
|
break;
|
|
case FE_INEXACT:
|
|
*vector = __trap_vectors.inexact;
|
|
break;
|
|
case FE_UNDERFLOW:
|
|
*vector = __trap_vectors.underflow;
|
|
break;
|
|
case FE_DENORM_OP:
|
|
*vector = __trap_vectors.denorm_op;
|
|
break;
|
|
default:
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
<P>
|
|
<P>
|
|
/*
|
|
Set the trap vector for the specified exception.
|
|
*/
|
|
int fesetvector(ftrap_t *vector, int excep)
|
|
{
|
|
switch ( excep )
|
|
{
|
|
case FE_STACK_FLT:
|
|
__trap_vectors.stack_flt = *vector;
|
|
break;
|
|
case FE_INVALID:
|
|
__trap_vectors.invalid = *vector;
|
|
break;
|
|
case FE_DIVBYZERO:
|
|
__trap_vectors.divbyzero = *vector;
|
|
break;
|
|
case FE_OVERFLOW:
|
|
__trap_vectors.overflow = *vector;
|
|
break;
|
|
case FE_INEXACT:
|
|
__trap_vectors.inexact = *vector;
|
|
break;
|
|
case FE_UNDERFLOW:
|
|
__trap_vectors.underflow = *vector;
|
|
break;
|
|
case FE_DENORM_OP:
|
|
__trap_vectors.denorm_op = *vector;
|
|
break;
|
|
default:
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
<P>
|
|
#define FE_REASONABLE_EXCEPT ( FE_INEXACT | FE_DIVBYZERO | FE_OVERFLOW | FE_INVALID )
|
|
<P>
|
|
void fpenab_ () {
|
|
feenabletraps(FE_REASONABLE_EXCEPT);
|
|
}
|
|
---------------------------------------------------------------------------
|
|
This library can be also called from FORTRAN files as follow:
|
|
---------------------------------------------------------------------------
|
|
program test
|
|
external hanfpe
|
|
call signal(8,hanfpe)
|
|
call fpenab
|
|
call mess3
|
|
end
|
|
subroutine mess1
|
|
a=0.
|
|
b=1/a
|
|
end
|
|
subroutine mess2
|
|
a=0.
|
|
b=log(a)
|
|
end
|
|
subroutine mess3
|
|
a=-1
|
|
b=sqrt(a)
|
|
end
|
|
subroutine mess4
|
|
i=0
|
|
b=1/i
|
|
end
|
|
subroutine hanfpe
|
|
print *, 'We have a floating point excp...'
|
|
print *, 'Bye Bye....'
|
|
stop
|
|
end
|