/* This is -*- C -*- */
/* vim: set sw=2: */
/* $Id: gnan.c,v 1.9 2002/01/14 05:01:23 trow Exp $ */
/*
* gnan.c
*
* Copyright (C) 2000 EMC Capital Management, Inc.
*
* Developed by Jon Trowbridge <trow@gnu.org>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA
*/
#include <config.h>
#include "gnan.h"
#include <string.h>
#include <signal.h>
#include <glib.h>
#include "guppi-debug.h"
union ieee754_big_endian_double {
double d;
struct {
unsigned int signbit:1;
unsigned int exp:11;
unsigned int man0:20;
unsigned int man1:32;
} ieee;
};
union ieee754_little_endian_double {
double d;
struct {
unsigned int man1:32;
unsigned int man0:20;
unsigned int exp:11;
unsigned int signbit:1;
} ieee;
};
/* little endian, but with big endian float word order */
union ieee754_mixed_endian_double {
double d;
struct {
unsigned int man0:20;
unsigned int exp:11;
unsigned int signbit:1;
unsigned int man1:32;
} ieee;
};
#define ALPHA_TEST_VALUE 314159.314159
/* This is the bit-representation of ALPHA_TEST_VALUE under Alpha's
non-IEEE FP. */
/* These values are WRONG */
static const guint alpha_test_bits[8] = {
0x9b, 0xe5, 0xb2, 0x41, 0xbd, 0x2c, 0x13, 0x41
};
static const guint alpha_nan_bits_A[8] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x7f
};
static const guint alpha_nan_bits_B[8] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xff
};
const double G_NAN;
#ifndef NAN
static gboolean fake_isnan = FALSE;
#endif
#define FAKE_NAN 123.45678902468013579e-300
#define NUMBER 1.0
#define SIGNBIT 0
#define MAN0 0
#define MAN1 0
#define EXP 1023
gboolean
g_isnan (double x)
{
#ifndef NAN
return fake_isnan ? (x == FAKE_NAN) : isnan (x);
#else
return isnan (x);
#endif
}
gboolean
g_isinf (double x)
{
/* FIXME: This needs to be made portable. */
return isinf (x);
}
gboolean
g_finite (double x)
{
/* FIXME: This needs to be made portable. */
return finite (x);
}
void
gnan_init (void)
{
#ifdef NAN
double d = NAN;
/* If we have NAN, we just use it. */
g_assert (isnan (NAN));
memcpy ((double *)&G_NAN, &d, sizeof (double));
#else
union ieee754_big_endian_double be;
union ieee754_little_endian_double le;
union ieee754_mixed_endian_double me;
double d;
/* Note: we declared NAN const so we have to be a bit careful about
changing it. Otherwise we outsmart the compiler and thus
ourselves. Not good. */
be.d = NUMBER;
if (sizeof (double) == 8
&& be.ieee.signbit == SIGNBIT
&& be.ieee.man0 == MAN0
&& be.ieee.man1 == MAN1
&& be.ieee.exp == EXP) {
be.ieee.signbit = 0;
be.ieee.man0 = 1;
be.ieee.man1 = 1;
be.ieee.exp = 2047;
memcpy ((double *)&G_NAN, &be.d, sizeof (double));
if (isnan (G_NAN)) return;
}
le.d = NUMBER;
if (sizeof (double) == 8
&& le.ieee.signbit == SIGNBIT
&& le.ieee.man0 == MAN0
&& le.ieee.man1 == MAN1
&& le.ieee.exp == EXP) {
le.ieee.signbit = 0;
le.ieee.man0 = 1;
le.ieee.man1 = 1;
le.ieee.exp = 2047;
memcpy ((double *)&G_NAN, &le.d, sizeof (double));
if (isnan (G_NAN)) return;
}
me.d = NUMBER;
if (sizeof (double) == 8
&& me.ieee.signbit == SIGNBIT
&& me.ieee.man0 == MAN0
&& me.ieee.man1 == MAN1
&& me.ieee.exp == EXP) {
me.ieee.signbit = 0;
me.ieee.man0 = 1;
me.ieee.man1 = 1;
me.ieee.exp = 2047;
memcpy ((double *)&G_NAN, &me.d, sizeof (double));
if (isnan (G_NAN)) return;
}
/* Morten Welinder tells me that "Some people here argue that the
specs guarantee a NaN to come out of this." It is certainly
worth a try... */
{
double a = -HUGE_VAL;
double b = 0;
*(double *)&G_NAN = a * b;
if (isnan (G_NAN)) return;
}
/* This is abusive, but I need a quick and dirty way of
handling Alpha FP right away... */
d = ALPHA_TEST_VALUE;
if (sizeof (double) == 8) {
gint i;
gboolean ok = TRUE;
for (i=0; i<sizeof(double) && ok; ++i) {
if (alpha_test_bits[i] != (guint)(((guchar *)&d)[i]))
ok = FALSE;
}
if (ok) {
memcpy ((gpointer)&G_NAN, alpha_nan_bits_A, 8);
if (isnan (G_NAN)) return;
memcpy ((gpointer)&G_NAN, alpha_nan_bits_B, 8);
if (isnan (G_NAN)) return;
}
}
/* This is crazy, but we are desparate at this point... */
{
struct sigaction fpe, old_fpe;
fpe.sa_handler = SIG_IGN;
sigaction (SIGFPE, &fpe, &old_fpe);
d = sqrt (-1.0);
sigaction (SIGFPE, &old_fpe, NULL);
if (isnan (d)) {
memcpy ((gpointer)&G_NAN, &d, sizeof (double));
return;
}
}
/* All else has failed, so we have to use the awful FAKE_NAN hack. */
d = FAKE_NAN;
fake_isnan = TRUE;
memcpy ((gpointer)&G_NAN, &d, sizeof (double));
g_assert (g_isnan (G_NAN));
guppi_msg ("Using awful FAKE_NAN hack.");
#endif /* NAN */
}
/* $Id: gnan.c,v 1.9 2002/01/14 05:01:23 trow Exp $ */
syntax highlighted by Code2HTML, v. 0.9.1