/***********************************************************************

This is a test program to test what floating-point exceptions are
raised during floating-point multiply and multiply-add for 0 and
Infinity operands.

The code is complete for native compilers on these systems:

IBM RS/6000 AIX 4.x:
	% cc ofl.c && ./a.out

	AIX bottom 2 4 003814264C00
	IBM RS6000 43P-132 (133 MHz);  AIX 4.2


	Begin test: result = inf*zero
	Floating-point exception flags = 0xa0100000 [ FP_INV_IMZ (Infinity * 0) ]
	Inf = INF       Zero = 0        Inf*Zero = NaNQ


	Begin test: result = one + inf*zero
	Floating-point exception flags = 0xa0100000 [ FP_INV_IMZ (Infinity * 0) ]
	One = 1 Inf = INF       Zero = 0        One + Inf*Zero = NaNQ


	End tests

Intel x86, any O/S with GNU gcc compiler:
	% gcc -g ofl.c && ./a.out
	Linux XXXXXXXX.YYYYYYYY.utah.edu 2.2.17-14 #1 Mon Feb 5 18:48:50 EST 2001 i686 unknown
	Intel Pentium III (600MHz);    GNU/Linux 2.2.16-3smp (Red Hat 6.2)


	Begin test: result = inf*zero
	Floating-point exception flags = 0x00000001 [ FP_INVALID_OPERATION ]
	Inf = inf       Zero = 0        Inf*Zero = nan
	Floating-point exception flags = 0x00000100 []


	Begin test: result = one + inf*zero
	Floating-point exception flags = 0x00000101 [ FP_INVALID_OPERATION ]
	One = 1 Inf = inf       Zero = 0        One + Inf*Zero = nan


	End tests

	% gcc -g ofl.c && ./a.out
	FreeBSD XXXXXXXX.YYYYYYYY.utah.edu 4.1.1-RELEASE FreeBSD 4.1.1-RELEASE #0: Fri N i386 unknown
	Intel Pentium II 450MHz (2 CPUs);       FreeBSD 4.1.1


	Begin test: result = inf*zero
	Floating-point exception flags = 0x00000001 [ FP_INVALID_OPERATION ]
	Inf = Inf       Zero = 0        Inf*Zero = NaN


	Begin test: result = one + inf*zero
	Floating-point exception flags = 0x00000001 [ FP_INVALID_OPERATION ]
	One = 1 Inf = Inf       Zero = 0        One + Inf*Zero = NaN


	End tests


SGI MIPS Rx0000 IRIX 6.5:
	% cc ofl.c && ./a.out
	IRIX64 XXXXXXXX.YYYYYYYY 6.5 04151556 IP27 mips
	SGI Origin/200-4;              IRIX 6.5


	Begin test: result = inf*zero
	Floating-point exception flags = 0x00010040 [ ex_invalid  se_invalid ]
	Inf = inf       Zero = 0        Inf*Zero = nan0x7fffffff


	Begin test: result = one + inf*zero
	Floating-point exception flags = 0x00000040 [ se_invalid ]
	One = 1 Inf = inf       Zero = 0        One + Inf*Zero = nan0x7fffffff


	End tests


Sun SPARC Solaris 2.7:
	# NB: Notice the extra runtime library, -lsunmath, needed for
	# the ieee_xxx() functions!
	% cc ofl.c -lsunmath -lm && ./a.out
	SunOS XXXXXXXX.YYYYYYYY.utah.edu 5.7 Generic_106541-12 sun4u sparc SUNW,Ultra-Enterprise
	Sun Ultra Enterprise 5500;     Solaris 2.7


	Begin test: result = inf*zero
	Floating-point exception flags = 0x00000010 [ fp_invalid ]
	Inf = Inf       Zero = 0        Inf*Zero = NaN


	Begin test: result = one + inf*zero
	Floating-point exception flags = 0x00000010 [ fp_invalid ]
	One = 1 Inf = Inf       Zero = 0        One + Inf*Zero = NaN


	End tests

Sun Solaris 2.x on Intel x86 and Apple/IBM/Motorola PowerPC:
	code present, but untested

More is still needed for these:

Compaq/DEC Alpha OSF/1 4.0:
	Code incomplete: I still need to learn how to recover from
	an exception by throwing it on to the standard library, and
	also how to get the floating-point flags at the time of the
	exception.

Author:
	Nelson H. F. Beebe
	Center for Scientific Computing
	University of Utah
	Department of Mathematics, 322 INSCC
	155 S 1400 E RM 233
	Salt Lake City, UT 84112-0090
	USA
	Email: beebe@math.utah.edu, beebe@acm.org, beebe@computer.org, beebe@ieee.org (Internet)
	WWW URL: http://www.math.utah.edu/~beebe
	Telephone: +1 801 581 5254
	FAX: +1 801 585 1640, +1 801 581 4148

[27-Feb-2001]
***********************************************************************/

#define PRINTF (void)printf
#define SYSTEM (void)system

#if defined(__STDC__) || defined(__cplusplus)
#define ARGS(parenthesized_list) parenthesized_list
#else
#define ARGS(parenthesized_list) ()
#endif

#include <stdio.h>
#include <stdlib.h>

#ifndef EXIT_SUCCESS
#define EXIT_SUCCESS 0
#endif

#if defined(__GNUC__) && defined(__i386__)
#define FP_INVALID_OPERATION		0x0001
#define FP_DENORMALIZED_OPERAND		0x0002
#define FP_ZERO_DIVIDE			0x0004
#define FP_OVERFLOW			0x0008
#define FP_UNDERFLOW			0x0010
#define FP_PRECISION			0x0020
#define FP_ALL_EXCEPTIONS		0x003f /* OR of all the above */
#endif

#if defined(_IBMR2)
#include <fpxcp.h>
#endif

#if defined(__osf__)
#include <machine/fpu.h>
#include <signal.h>
#endif

#if defined(__sun)
#include <sys/ieeefp.h>
#endif

#if !defined(_IBMR2)
typedef unsigned long fpflag_t;
#endif

#if defined(__osf__)
static fpflag_t fp_flag_OSF_Alpha;
#endif

#if defined(__sgi)
#include <sigfpe.h>
#include <sys/fpu.h>
#endif

int		main ARGS((void));
static void	clear_fp_flags ARGS((void));
static void	fp_handler ARGS((int sig));
static fpflag_t	get_fp_flags ARGS((void));
static void	initialize ARGS((void));
static void	print_fp_flags ARGS((fpflag_t mask));

int
main(void)
{
    double inf, one, result, zero;

    initialize();

    clear_fp_flags();
    print_fp_flags(get_fp_flags());

    inf = 1.0;
    zero = 0.0;
    inf = inf/zero;

    SYSTEM("uname -a");
    /* SYSTEM("machinetype"); */	/* enable only at developer's site */

    /*----------------------------------------------------------------*/

    clear_fp_flags();
    print_fp_flags(get_fp_flags());

    PRINTF("\n\nBegin test: result = inf*zero\n");
    result = inf*zero;
    print_fp_flags(get_fp_flags());

    PRINTF("Inf = %g\tZero = %g\tInf*Zero = %g\n",
		 inf, zero, result);

    /*----------------------------------------------------------------*/

    clear_fp_flags();
    print_fp_flags(get_fp_flags());
    PRINTF("\n\nBegin test: result = one + inf*zero\n");

    one = 1.0;
    result = one + inf*zero;
    print_fp_flags(get_fp_flags());

    PRINTF("One = %g\tInf = %g\tZero = %g\tOne + Inf*Zero = %g\n",
		 one, inf, zero, result);

    PRINTF("\n\nEnd tests\n");

    return (EXIT_SUCCESS);
}


static void
clear_fp_flags(void)
{
#if defined(__GNUC__) && defined(__i386__)
    __asm __volatile ("fclex"); /* clear exception flags to avoid interrupt on next fldcw! */
#elif defined(_IBMR2)
    fp_clr_flag(0 |
		FP_INVALID |
		FP_OVERFLOW |
		FP_UNDERFLOW |
		FP_DIV_BY_ZERO |
		FP_INEXACT |
		FP_INV_SNAN |	/* Signalling NaN */
		FP_INV_ISI |	/* Infinity - Infinity */
		FP_INV_IDI |	/* Infinity / Infinity */
		FP_INV_ZDZ |	/* 0/0 */
		FP_INV_IMZ |	/* Infinity * 0 */
		FP_INV_CMP |	/* Unordered Compare */
		FP_INV_SQRT |	/* Square Root of negative number */
		FP_INV_CVI |	/* Conversion to integer error */
		FP_INV_VXSOFT |	/* Software request */
		0);
#elif defined(__osf__)
    ieee_set_fp_control(IEEE_TRAP_ENABLE_MASK);	/* enable all fp interrupts */
#elif defined(__sgi)
    (void)set_fpc_csr(0);
#elif defined(__sun)
    char *out;
    ieee_flags ("clear","exception","all",&out);
#endif
}


static void
fp_handler(int signo)
{
#if defined(__osf__)
    if (signo == SIGFPE)
    {
        fp_flag_OSF_Alpha |= 0;
    }
#endif
}


static fpflag_t
get_fp_flags(void)
{
#if defined(__GNUC__) && defined(__i386__)
    static short int flags;
    __asm __volatile ("fstsw %0" : : "m" (flags));
    /* Return only the exception flag bits, ignoring all others */
    return ((fpflag_t)(flags & FP_ALL_EXCEPTIONS));
#elif defined(_IBMR2)
    return (fp_read_flag());
#elif defined(__sun)
    char *out;
    return (ieee_flags("get","exception","overflow",&out));
#elif defined(__osf__)
    return (fp_flag_OSF_Alpha);
#elif defined(__sgi)
    return ((fpflag_t)get_fpc_csr());
#else
    return ((fpflag_t)0);
#endif
}


static void
initialize()
{
#if defined(__GNUC__) && defined(__i386__)
    clear_fp_flags();
#elif defined(__osf__)
    if (signal(SIGFPE,fp_handler) == SIG_ERR)
	(void)fprintf(stderr,"ERROR: Cannot catch SIGFPE exceptions\n");
#endif
}


static void
print_fp_flags(fpflag_t mask)
{
#if defined(__sgi)
    union fpc_csr csr;
#endif
    if (mask != (fpflag_t)0)
    {
	PRINTF("Floating-point exception flags = 0x%08lx [",
		     (unsigned long)mask);
#if defined(__GNUC__) && defined(__i386__)
	if (mask & FP_INVALID_OPERATION)    PRINTF(" FP_INVALID_OPERATION ");
	if (mask & FP_DENORMALIZED_OPERAND) PRINTF(" FP_DENORMALIZED_OPERAND ");
	if (mask & FP_ZERO_DIVIDE)          PRINTF(" FP_ZERO_DIVIDE ");
	if (mask & FP_OVERFLOW)             PRINTF(" FP_OVERFLOW ");
	if (mask & FP_UNDERFLOW)            PRINTF(" FP_UNDERFLOW ");
	if (mask & FP_PRECISION)            PRINTF(" FP_PRECISION ");
#elif defined(_IBMR2)
	if (mask & FP_INEXACT)              PRINTF(" FP_INEXACT ");
	if (mask & FP_OVERFLOW)             PRINTF(" FP_OVERFLOW ");
	if (mask & FP_UNDERFLOW)            PRINTF(" FP_UNDERFLOW ");
	if (mask & FP_DIV_BY_ZERO)          PRINTF(" FP_DIV_BY_ZERO ");
	if (mask & FP_INEXACT)              PRINTF(" FP_INEXACT ");
	if (mask & FP_INV_SNAN)             PRINTF(" FP_INV_SNAN (Signalling NaN) ");
	if (mask & FP_INV_ISI)              PRINTF(" FP_INV_ISI (Infinity - Infinity) ");
	if (mask & FP_INV_IDI)              PRINTF(" FP_INV_IDI (Infinity / Infinity) ");
	if (mask & FP_INV_ZDZ)              PRINTF(" FP_INV_ZDZ (0/0) ");
	if (mask & FP_INV_IMZ)              PRINTF(" FP_INV_IMZ (Infinity * 0) ");
	if (mask & FP_INV_CMP)              PRINTF(" FP_INV_CMP (Unordered Compare) ");
	if (mask & FP_INV_SQRT)             PRINTF(" FP_INV_SQRT (Square Root of negative number) ");
	if (mask & FP_INV_CVI)              PRINTF(" FP_INV_CVI (Conversion to integer error) ");
	if (mask & FP_INV_VXSOFT)           PRINTF(" FP_INV_VXSOFT (Software request) ");
#elif defined(__sgi)
	csr.fc_word = mask;
	/* Mostly-recent set exception flags */
	if (csr.fc_struct.ex_unimplemented) PRINTF(" ex_unimplemented ");
	if (csr.fc_struct.ex_invalid)       PRINTF(" ex_invalid ");
	if (csr.fc_struct.ex_divide0)       PRINTF(" ex_divide0 ");
	if (csr.fc_struct.ex_overflow)      PRINTF(" ex_overflow ");
	if (csr.fc_struct.ex_underflow)     PRINTF(" ex_underflow ");
	if (csr.fc_struct.ex_inexact)       PRINTF(" ex_inexact ");
	/* Historically-set exception flags */
	if (csr.fc_struct.se_invalid)       PRINTF(" se_invalid ");
	if (csr.fc_struct.se_divide0)       PRINTF(" se_divide0 ");
	if (csr.fc_struct.se_overflow)      PRINTF(" se_overflow ");
	if (csr.fc_struct.se_underflow)     PRINTF(" se_underflow ");
	if (csr.fc_struct.se_inexact)       PRINTF(" se_inexact ");
#elif defined(__sun) && defined(__sparc)
	if (mask & (1L << fp_inexact))      PRINTF(" fp_inexact ");
	if (mask & (1L << fp_division))     PRINTF(" fp_division ");
	if (mask & (1L << fp_underflow))    PRINTF(" fp_underflow ");
	if (mask & (1L << fp_overflow))     PRINTF(" fp_overflow ");
	if (mask & (1L << fp_invalid))      PRINTF(" fp_invalid ");
#elif defined(__sun) && defined(__i386)
	if (mask & (1L << fp_invalid))      PRINTF(" fp_invalid ");
	if (mask & (1L << fp_denormalized)) PRINTF(" fp_denormalized ");
	if (mask & (1L << fp_division))     PRINTF(" fp_division ");
	if (mask & (1L << fp_overflow))     PRINTF(" fp_overflow ");
	if (mask & (1L << fp_underflow))    PRINTF(" fp_underflow ");
	if (mask & (1L << fp_inexact))      PRINTF(" fp_inexact ");
#elif defined(__sun) && defined(__ppc)
	if (mask & (1L << fp_invalid_cvi))  PRINTF(" fp_invalid_cvi ");
	if (mask & (1L << fp_invalid_sqrt)) PRINTF(" fp_invalid_sqrt ");
	if (mask & (1L << fp_invalid_soft)) PRINTF(" fp_invalid_soft ");
	if (mask & (1L << fp_invalid_vc))   PRINTF(" fp_invalid_vc ");
	if (mask & (1L << fp_invalid_imz))  PRINTF(" fp_invalid_imz ");
	if (mask & (1L << fp_invalid_zdz))  PRINTF(" fp_invalid_zdz ");
	if (mask & (1L << fp_invalid_idi))  PRINTF(" fp_invalid_idi ");
	if (mask & (1L << fp_invalid_isi))  PRINTF(" fp_invalid_isi ");
	if (mask & (1L << fp_invalid_snan)) PRINTF(" fp_invalid_snan ");
	if (mask & (1L << fp_inexact))      PRINTF(" fp_inexact ");
	if (mask & (1L << fp_division))     PRINTF(" fp_division ");
	if (mask & (1L << fp_underflow))    PRINTF(" fp_underflow ");
	if (mask & (1L << fp_overflow))     PRINTF(" fp_overflow ");
	if (mask & (1L << fp_invalid))      PRINTF(" fp_invalid ");
#endif
	PRINTF("]\n");
    }
}
