From 7080851f5a8270aee50e8545d28c48c6c75decc1 Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Sat, 12 Jul 2008 10:50:41 -0700 Subject: [PATCH] Add isnan module. --- ChangeLog | 20 ++++ MODULES.html.sh | 1 + doc/posix-functions/isnan.texi | 16 ++- lib/math.in.h | 35 +++++++ m4/isnan.m4 | 32 ++++++ m4/isnanl.m4 | 13 ++- m4/math_h.m4 | 2 + modules/isnan | 27 +++++ modules/isnan-tests | 16 +++ modules/math | 2 + tests/test-isnan.c | 228 +++++++++++++++++++++++++++++++++++++++++ 11 files changed, 387 insertions(+), 5 deletions(-) create mode 100644 m4/isnan.m4 create mode 100644 modules/isnan create mode 100644 modules/isnan-tests create mode 100644 tests/test-isnan.c diff --git a/ChangeLog b/ChangeLog index ddc045aaa..3f943e761 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,25 @@ 2008-07-10 Ben Pfaff + Add isnan module. + * doc/posix-functions/isnan.texi: Mention new module. + * lib/math.in.h: Define isnan macro if we have decided to replace + it. + * m4/isnan.m4: New file. + * m4/isnanl.m4 (gl_FUNC_ISNANL): Factor out some code into new + macro gl_BUILD_ISNANL so that isnan.m4 can use that functionality + also. + (gl_FUNC_ISNANL_NO_LIBM): Factor out same code, to reduce + redundancy. + * m4/math_h.m4: Initialize and substitute variables for isnan + module. + * modules/isnan: New file. + * modules/isnan-tests: New file. + * modules/math: Add substitutions for new module. + * tests/test-isnan.c: New file. + * MODULES.html.sh: Mention new module. + +2008-07-10 Ben Pfaff + Add isnanf module. * lib/isnanf.m4: New file. * m4/isnanf.m4 (gl_FUNC_ISNANF): New macro. diff --git a/MODULES.html.sh b/MODULES.html.sh index 58a19d5c9..8108e3ff4 100755 --- a/MODULES.html.sh +++ b/MODULES.html.sh @@ -1999,6 +1999,7 @@ func_all_modules () func_module frexpl func_module frexpl-nolibm func_module isfinite + func_module isnan func_module isnanf func_module isnanf-nolibm func_module isnand diff --git a/doc/posix-functions/isnan.texi b/doc/posix-functions/isnan.texi index 90395c7d3..01816868c 100644 --- a/doc/posix-functions/isnan.texi +++ b/doc/posix-functions/isnan.texi @@ -4,10 +4,24 @@ POSIX specification: @url{http://www.opengroup.org/susv3xsh/isnan.html} -Gnulib module: --- +Gnulib module: isnan Portability problems fixed by Gnulib: @itemize +@item +@code{isnan} was introduced with C99 and is thus commonly not present +on pre-C99 systems. +@item +On IRIX 6.5 with @code{cc}, @code{isnan} does not recognize some NaNs. +@item +On NetBSD/i386 and glibc/ia64, @code{isnan} does not recognize some +forms of NaNs, such as pseudo-NaNs, pseudo-Infinities, and +unnormalized numbers. +@item +On i686 and @var{x}86-64, @code{__builtin_isnanl} (and thus +@code{isnan} implementations based on it) in GCC 4.0 and later does +not recognize pseudo-denormals as NaNs, and similarly for +pseudo-zeroes, unnormalized numbers, and pseudo-denormals on ia64. @end itemize Portability problems not fixed by Gnulib: diff --git a/lib/math.in.h b/lib/math.in.h index f50082481..85f37855c 100644 --- a/lib/math.in.h +++ b/lib/math.in.h @@ -381,6 +381,41 @@ extern int gl_isfinitel (long double x); #endif +#if @GNULIB_ISNAN@ +# if @REPLACE_ISNAN@ +/* We can't just use the isnanf macro (e.g.) as exposed by + isnanf.h (e.g.) here, because those may end up being macros + that recursively expand back to isnan. So use the gnulib + replacements for them directly. */ +# if HAVE_ISNANF && __GNUC__ >= 4 +# define gl_isnan_f(x) __builtin_isnan ((float)(x)) +# else +extern int rpl_isnanf (float x); +# define gl_isnan_f(x) rpl_isnanf (x) +# endif +# if HAVE_ISNAND && __GNUC__ >= 4 +# define gl_isnan_d(x) __builtin_isnan ((double)(x)) +# else +extern int rpl_isnand (double x); +# define gl_isnan_d(x) rpl_isnand (x) +# endif +# if HAVE_ISNANL && __GNUC__ >= 4 +# define gl_isnan_l(x) __builtin_isnan ((long double)(x)) +# else +extern int rpl_isnanl (long double x); +# define gl_isnan_l(x) rpl_isnanl (x) +# endif +# undef isnan +# define isnan(x) \ + (sizeof (x) == sizeof (long double) ? gl_isnan_l (x) : \ + sizeof (x) == sizeof (double) ? gl_isnan_d (x) : \ + gl_isnan_f (x)) +# endif +#elif defined GNULIB_POSIXCHECK + /* How to override a macro? */ +#endif + + #if @GNULIB_SIGNBIT@ # if @REPLACE_SIGNBIT_USING_GCC@ # undef signbit diff --git a/m4/isnan.m4 b/m4/isnan.m4 new file mode 100644 index 000000000..f146601cd --- /dev/null +++ b/m4/isnan.m4 @@ -0,0 +1,32 @@ +# isnan.m4 serial 1 +dnl Copyright (C) 2007-2008 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +AC_DEFUN([gl_ISNAN], +[ + AC_REQUIRE([gl_MATH_H_DEFAULTS]) + AC_REQUIRE([gl_FUNC_ISNANF]) + AC_REQUIRE([gl_FUNC_ISNAND]) + AC_REQUIRE([gl_FUNC_ISNANL]) + + ISNAN_LIBM="$ISNANF_LIBM $ISNAND_LIBM $ISNANL_LIBM" + AC_SUBST([ISNAN_LIBM]) + + # If we replaced any of the underlying isnan* functions, replace + # the isnan macro; it undoubtedly suffers from the same flaws. + AC_MSG_CHECKING([whether isnan macro works]) + if test $gl_func_isnanf = yes \ + && test $gl_func_isnand = yes \ + && test $gl_func_isnanl = yes; then + AC_MSG_RESULT([yes]) + else + # Make sure the rpl_isnan[fdl] functions get built. + gl_BUILD_ISNANF + gl_BUILD_ISNAND + gl_BUILD_ISNANL + REPLACE_ISNAN=1 + AC_MSG_RESULT([no]) + fi +]) diff --git a/m4/isnanl.m4 b/m4/isnanl.m4 index 660a898a3..64f70d206 100644 --- a/m4/isnanl.m4 +++ b/m4/isnanl.m4 @@ -31,8 +31,7 @@ AC_DEFUN([gl_FUNC_ISNANL], AC_DEFINE([HAVE_ISNANL], 1, [Define if the isnan(long double) function is available.]) else - AC_LIBOBJ([isnanl]) - gl_LONG_DOUBLE_EXPONENT_LOCATION + gl_BUILD_ISNANL fi AC_SUBST([ISNANL_LIBM]) ]) @@ -52,11 +51,17 @@ AC_DEFUN([gl_FUNC_ISNANL_NO_LIBM], AC_DEFINE([HAVE_ISNANL_IN_LIBC], 1, [Define if the isnan(long double) function is available in libc.]) else - AC_LIBOBJ([isnanl]) - gl_LONG_DOUBLE_EXPONENT_LOCATION + gl_BUILD_ISNANL fi ]) +dnl Pull in replacement isnanl definition. +AC_DEFUN([gl_BUILD_ISNANL], +[ + AC_LIBOBJ([isnanl]) + gl_LONG_DOUBLE_EXPONENT_LOCATION +]) + dnl Test whether isnanl() can be used without libm. AC_DEFUN([gl_HAVE_ISNANL_NO_LIBM], [ diff --git a/m4/math_h.m4 b/m4/math_h.m4 index 902fdb57e..0348f24be 100644 --- a/m4/math_h.m4 +++ b/m4/math_h.m4 @@ -51,6 +51,7 @@ AC_DEFUN([gl_MATH_H_DEFAULTS], GNULIB_FREXP=0; AC_SUBST([GNULIB_FREXP]) GNULIB_FREXPL=0; AC_SUBST([GNULIB_FREXPL]) GNULIB_ISFINITE=0; AC_SUBST([GNULIB_ISFINITE]) + GNULIB_ISNAN=0; AC_SUBST([GNULIB_ISNAN]) GNULIB_LDEXPL=0; AC_SUBST([GNULIB_LDEXPL]) GNULIB_MATHL=0; AC_SUBST([GNULIB_MATHL]) GNULIB_ROUND=0; AC_SUBST([GNULIB_ROUND]) @@ -82,6 +83,7 @@ AC_DEFUN([gl_MATH_H_DEFAULTS], REPLACE_FREXPL=0; AC_SUBST([REPLACE_FREXPL]) REPLACE_HUGE_VAL=0; AC_SUBST([REPLACE_HUGE_VAL]) REPLACE_ISFINITE=0; AC_SUBST([REPLACE_ISFINITE]) + REPLACE_ISNAN=0; AC_SUBST([REPLACE_ISNAN]) REPLACE_LDEXPL=0; AC_SUBST([REPLACE_LDEXPL]) REPLACE_NAN=0; AC_SUBST([REPLACE_NAN]) REPLACE_ROUND=0; AC_SUBST([REPLACE_ROUND]) diff --git a/modules/isnan b/modules/isnan new file mode 100644 index 000000000..16972faac --- /dev/null +++ b/modules/isnan @@ -0,0 +1,27 @@ +Description: +isnan macro: test for NaN value. + +Files: +m4/isnan.m4 + +Depends-on: +isnanf +isnand +isnanl +math +extensions + +configure.ac: +gl_ISNAN +gl_MATH_MODULE_INDICATOR([isnan]) + +Makefile.am: + +Include: + + +License: +GPL + +Maintainer: +Ben Pfaff diff --git a/modules/isnan-tests b/modules/isnan-tests new file mode 100644 index 000000000..f06a78394 --- /dev/null +++ b/modules/isnan-tests @@ -0,0 +1,16 @@ +Files: +tests/test-isnan.c +tests/nan.h + +Depends-on: + +configure.ac: +gl_FLOAT_EXPONENT_LOCATION +gl_DOUBLE_EXPONENT_LOCATION +gl_LONG_DOUBLE_EXPONENT_LOCATION + +Makefile.am: +TESTS += test-isnan +check_PROGRAMS += test-isnan +test_isnan_LDADD = $(LDADD) @ISNAN_LIBM@ + diff --git a/modules/math b/modules/math index f3a38dae5..4be850150 100644 --- a/modules/math +++ b/modules/math @@ -29,6 +29,7 @@ math.h: math.in.h -e 's|@''GNULIB_FREXP''@|$(GNULIB_FREXP)|g' \ -e 's|@''GNULIB_FREXPL''@|$(GNULIB_FREXPL)|g' \ -e 's|@''GNULIB_ISFINITE''@|$(GNULIB_ISFINITE)|g' \ + -e 's|@''GNULIB_ISNAN''@|$(GNULIB_ISNAN)|g' \ -e 's|@''GNULIB_LDEXPL''@|$(GNULIB_LDEXPL)|g' \ -e 's|@''GNULIB_MATHL''@|$(GNULIB_MATHL)|g' \ -e 's|@''GNULIB_ROUND''@|$(GNULIB_ROUND)|g' \ @@ -59,6 +60,7 @@ math.h: math.in.h -e 's|@''REPLACE_FREXPL''@|$(REPLACE_FREXPL)|g' \ -e 's|@''REPLACE_HUGE_VAL''@|$(REPLACE_HUGE_VAL)|g' \ -e 's|@''REPLACE_ISFINITE''@|$(REPLACE_ISFINITE)|g' \ + -e 's|@''REPLACE_ISNAN''@|$(REPLACE_ISNAN)|g' \ -e 's|@''REPLACE_LDEXPL''@|$(REPLACE_LDEXPL)|g' \ -e 's|@''REPLACE_NAN''@|$(REPLACE_NAN)|g' \ -e 's|@''REPLACE_ROUND''@|$(REPLACE_ROUND)|g' \ diff --git a/tests/test-isnan.c b/tests/test-isnan.c new file mode 100644 index 000000000..9761ee662 --- /dev/null +++ b/tests/test-isnan.c @@ -0,0 +1,228 @@ +/* Test of isnand() substitute. + Copyright (C) 2007-2008 Free Software Foundation, Inc. + + 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 3 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, see . */ + +/* Written by Ben Pfaff , from code by Bruno + Haible . */ + +#include + +#include + +#include +#include +#include +#include + +#include "nan.h" + +#define ASSERT(expr) \ + do \ + { \ + if (!(expr)) \ + { \ + fprintf (stderr, "%s:%d: assertion failed\n", __FILE__, __LINE__); \ + fflush (stderr); \ + abort (); \ + } \ + } \ + while (0) + +static void +test_float (void) +{ + /* Finite values. */ + ASSERT (!isnan (3.141f)); + ASSERT (!isnan (3.141e30f)); + ASSERT (!isnan (3.141e-30f)); + ASSERT (!isnan (-2.718f)); + ASSERT (!isnan (-2.718e30f)); + ASSERT (!isnan (-2.718e-30f)); + ASSERT (!isnan (0.0f)); + ASSERT (!isnan (-0.0f)); + /* Infinite values. */ + ASSERT (!isnan (1.0f / 0.0f)); + ASSERT (!isnan (-1.0f / 0.0f)); + /* Quiet NaN. */ + ASSERT (isnan (NaNf ())); +#if defined FLT_EXPBIT0_WORD && defined FLT_EXPBIT0_BIT + /* Signalling NaN. */ + { + #define NWORDSF \ + ((sizeof (float) + sizeof (unsigned int) - 1) / sizeof (unsigned int)) + typedef union { float value; unsigned int word[NWORDSF]; } memory_float; + memory_float m; + m.value = NaNf (); +# if FLT_EXPBIT0_BIT > 0 + m.word[FLT_EXPBIT0_WORD] ^= (unsigned int) 1 << (FLT_EXPBIT0_BIT - 1); +# else + m.word[FLT_EXPBIT0_WORD + (FLT_EXPBIT0_WORD < NWORDSF / 2 ? 1 : - 1)] + ^= (unsigned int) 1 << (sizeof (unsigned int) * CHAR_BIT - 1); +# endif + if (FLT_EXPBIT0_WORD < NWORDSF / 2) + m.word[FLT_EXPBIT0_WORD + 1] |= (unsigned int) 1 << FLT_EXPBIT0_BIT; + else + m.word[0] |= (unsigned int) 1; + ASSERT (isnan (m.value)); + } +#endif +} + +static void +test_double (void) +{ + /* Finite values. */ + ASSERT (!isnan (3.141)); + ASSERT (!isnan (3.141e30)); + ASSERT (!isnan (3.141e-30)); + ASSERT (!isnan (-2.718)); + ASSERT (!isnan (-2.718e30)); + ASSERT (!isnan (-2.718e-30)); + ASSERT (!isnan (0.0)); + ASSERT (!isnan (-0.0)); + /* Infinite values. */ + ASSERT (!isnan (1.0 / 0.0)); + ASSERT (!isnan (-1.0 / 0.0)); + /* Quiet NaN. */ + ASSERT (isnan (NaNd ())); +#if defined DBL_EXPBIT0_WORD && defined DBL_EXPBIT0_BIT + /* Signalling NaN. */ + { + #define NWORDSD \ + ((sizeof (double) + sizeof (unsigned int) - 1) / sizeof (unsigned int)) + typedef union { double value; unsigned int word[NWORDSD]; } memory_double; + memory_double m; + m.value = NaNd (); +# if DBL_EXPBIT0_BIT > 0 + m.word[DBL_EXPBIT0_WORD] ^= (unsigned int) 1 << (DBL_EXPBIT0_BIT - 1); +# else + m.word[DBL_EXPBIT0_WORD + (DBL_EXPBIT0_WORD < NWORDSD / 2 ? 1 : - 1)] + ^= (unsigned int) 1 << (sizeof (unsigned int) * CHAR_BIT - 1); +# endif + m.word[DBL_EXPBIT0_WORD + (DBL_EXPBIT0_WORD < NWORDSD / 2 ? 1 : - 1)] + |= (unsigned int) 1 << DBL_EXPBIT0_BIT; + ASSERT (isnan (m.value)); + } +#endif +} + +static void +test_long_double (void) +{ + #define NWORDSL \ + ((sizeof (long double) + sizeof (unsigned int) - 1) / sizeof (unsigned int)) + typedef union { unsigned int word[NWORDSL]; long double value; } + memory_long_double; + + /* Finite values. */ + ASSERT (!isnan (3.141L)); + ASSERT (!isnan (3.141e30L)); + ASSERT (!isnan (3.141e-30L)); + ASSERT (!isnan (-2.718L)); + ASSERT (!isnan (-2.718e30L)); + ASSERT (!isnan (-2.718e-30L)); + ASSERT (!isnan (0.0L)); + ASSERT (!isnan (-0.0L)); + /* Infinite values. */ + ASSERT (!isnan (1.0L / 0.0L)); + ASSERT (!isnan (-1.0L / 0.0L)); + /* Quiet NaN. */ + ASSERT (isnan (0.0L / 0.0L)); + +#if defined LDBL_EXPBIT0_WORD && defined LDBL_EXPBIT0_BIT + /* A bit pattern that is different from a Quiet NaN. With a bit of luck, + it's a Signalling NaN. */ + { + memory_long_double m; + m.value = 0.0L / 0.0L; +# if LDBL_EXPBIT0_BIT > 0 + m.word[LDBL_EXPBIT0_WORD] ^= (unsigned int) 1 << (LDBL_EXPBIT0_BIT - 1); +# else + m.word[LDBL_EXPBIT0_WORD + (LDBL_EXPBIT0_WORD < NWORDSL / 2 ? 1 : - 1)] + ^= (unsigned int) 1 << (sizeof (unsigned int) * CHAR_BIT - 1); +# endif + m.word[LDBL_EXPBIT0_WORD + (LDBL_EXPBIT0_WORD < NWORDSL / 2 ? 1 : - 1)] + |= (unsigned int) 1 << LDBL_EXPBIT0_BIT; + ASSERT (isnan (m.value)); + } +#endif + +#if ((defined __ia64 && LDBL_MANT_DIG == 64) || (defined __x86_64__ || defined __amd64__) || (defined __i386 || defined __i386__ || defined _I386 || defined _M_IX86 || defined _X86_)) +/* Representation of an 80-bit 'long double' as an initializer for a sequence + of 'unsigned int' words. */ +# ifdef WORDS_BIGENDIAN +# define LDBL80_WORDS(exponent,manthi,mantlo) \ + { ((unsigned int) (exponent) << 16) | ((unsigned int) (manthi) >> 16), \ + ((unsigned int) (manthi) << 16) | (unsigned int) (mantlo) >> 16), \ + (unsigned int) (mantlo) << 16 \ + } +# else +# define LDBL80_WORDS(exponent,manthi,mantlo) \ + { mantlo, manthi, exponent } +# endif + { /* Quiet NaN. */ + static memory_long_double x = + { LDBL80_WORDS (0xFFFF, 0xC3333333, 0x00000000) }; + ASSERT (isnan (x.value)); + } + { + /* Signalling NaN. */ + static memory_long_double x = + { LDBL80_WORDS (0xFFFF, 0x83333333, 0x00000000) }; + ASSERT (isnan (x.value)); + } + /* The isnan function should recognize Pseudo-NaNs, Pseudo-Infinities, + Pseudo-Zeroes, Unnormalized Numbers, and Pseudo-Denormals, as defined in + Intel IA-64 Architecture Software Developer's Manual, Volume 1: + Application Architecture. + Table 5-2 "Floating-Point Register Encodings" + Figure 5-6 "Memory to Floating-Point Register Data Translation" + */ + { /* Pseudo-NaN. */ + static memory_long_double x = + { LDBL80_WORDS (0xFFFF, 0x40000001, 0x00000000) }; + ASSERT (isnan (x.value)); + } + { /* Pseudo-Infinity. */ + static memory_long_double x = + { LDBL80_WORDS (0xFFFF, 0x00000000, 0x00000000) }; + ASSERT (isnan (x.value)); + } + { /* Pseudo-Zero. */ + static memory_long_double x = + { LDBL80_WORDS (0x4004, 0x00000000, 0x00000000) }; + ASSERT (isnan (x.value)); + } + { /* Unnormalized number. */ + static memory_long_double x = + { LDBL80_WORDS (0x4000, 0x63333333, 0x00000000) }; + ASSERT (isnan (x.value)); + } + { /* Pseudo-Denormal. */ + static memory_long_double x = + { LDBL80_WORDS (0x0000, 0x83333333, 0x00000000) }; + ASSERT (isnan (x.value)); + } +#endif +} + +int +main () +{ + test_float (); + test_double (); + test_long_double (); + return 0; +} -- 2.11.0