#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
