PSPP would like to use the isfinite macro in ISO C99 <math.h>. The following patch (not yet committed) adds a module that implements a substitute when the system does not provide isfinite. It tests out OK with glibc on GNU/Linux, both configured normally and when I manually force use of the substitute. I wanted to test it on a machine that actually lacks isfinite, but none of the testdrive machines run one of those OSes (based on doc/functions/isfinite.texi).
Two possible issues I see here are: * Is it acceptable for isfinite to raise an exception on a signaling NaN? By my analysis (see the patch to isfinite.texi), this is acceptable, standards-wise, but it is different from what glibc does. * Is it acceptable to #include <float.h> in and put inline functions in math.in.h? Nothing else does either of those, right now. If anyone thinks that either of those is a real problem, I will reformulate the module. diff --git a/MODULES.html.sh b/MODULES.html.sh index 11c4e3e..3f353a7 100755 --- a/MODULES.html.sh +++ b/MODULES.html.sh @@ -1970,6 +1970,7 @@ func_all_modules () func_module frexp func_module frexpl func_module frexpl-nolibm + func_module isfinite func_module isnan-nolibm func_module isnanf-nolibm func_module isnanl diff --git a/doc/functions/isfinite.texi b/doc/functions/isfinite.texi index 2b777a6..1d1a667 100644 --- a/doc/functions/isfinite.texi +++ b/doc/functions/isfinite.texi @@ -4,15 +4,23 @@ POSIX specification: @url{http://www.opengroup.org/susv3xsh/isfinite.html} -Gnulib module: --- +Gnulib module: isfinite Portability problems fixed by Gnulib: @itemize [EMAIL PROTECTED] +This macro is missing on some platforms: +MacOS X 10.3, OpenBSD 3.8, AIX 5.1, IRIX 6.5, OSF/1 5.1, Solaris 10, Interix 3.5. @end itemize Portability problems not fixed by Gnulib: @itemize @item -This function is missing on some platforms: -MacOS X 10.3, OpenBSD 3.8, AIX 5.1, IRIX 6.5, OSF/1 5.1, Solaris 10, Interix 3.5. +POSIX defers to ISO C99 on the definition of this macro, ISO C99 +section F.3 in turn defers to the definition of the @code{finite} +function from IEC 60559, and the Appendix to IEC 60559 that defines [EMAIL PROTECTED] states that it is up to the implementation whether [EMAIL PROTECTED] is considered an arithmetic operation that raises an +exception for a signaling NaN operand. The Gnulib implementation will +raise an exception in this case, but glibc does not. @end itemize diff --git a/lib/math.in.h b/lib/math.in.h index c5d98f0..34491ab 100644 --- a/lib/math.in.h +++ b/lib/math.in.h @@ -334,6 +334,30 @@ extern long double truncl (long double x); truncl (x)) #endif +#if @GNULIB_ISFINITE@ +# if [EMAIL PROTECTED]@ +# include <float.h> +static inline int gl_isfinitef (float x) +{ + return x >= -FLT_MAX && x <= FLT_MAX; +} +static inline int gl_isfinited (double x) +{ + return x >= -DBL_MAX && x <= DBL_MAX; +} +static inline int gl_isfinitel (long double x) +{ + return x >= -LDBL_MAX && x <= LDBL_MAX; +} +# undef isfinite +# define isfinite(x) \ + (sizeof (x) == sizeof (long double) ? gl_isfinitel (x) : \ + sizeof (x) == sizeof (double) ? gl_isfinited (x) : \ + gl_isfinitef (x)) +# endif +#elif defined GNULIB_POSIXCHECK + /* How to override a macro? */ +#endif #if @GNULIB_SIGNBIT@ # if @REPLACE_SIGNBIT@ diff --git a/m4/isfinite.m4 b/m4/isfinite.m4 new file mode 100644 index 0000000..5058f95 --- /dev/null +++ b/m4/isfinite.m4 @@ -0,0 +1,21 @@ +# isfinite.m4 serial 1 +dnl Copyright (C) 2007 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_ISFINITE], +[ + AC_REQUIRE([gl_MATH_H_DEFAULTS]) + dnl Persuade glibc <math.h> to declare isfinite. + AC_REQUIRE([gl_USE_SYSTEM_EXTENSIONS]) + AC_CHECK_DECLS([isfinite], , , [#include <math.h>]) + if test "$ac_cv_have_decl_isfinite" = yes; then + gl_CHECK_MATH_LIB([ISFINITE_LIBM], [x = isfinite (x);]) + else + ISFINITE_LIBM= + HAVE_DECL_ISFINITE=0 + fi + AC_SUBST([HAVE_DECL_ISFINITE]) + AC_SUBST([ISFINITE_LIBM]) +]) diff --git a/m4/math_h.m4 b/m4/math_h.m4 index 4806e08..890888c 100644 --- a/m4/math_h.m4 +++ b/m4/math_h.m4 @@ -34,6 +34,7 @@ AC_DEFUN([gl_MATH_H_DEFAULTS], GNULIB_TRUNC=0; AC_SUBST([GNULIB_TRUNC]) GNULIB_TRUNCF=0; AC_SUBST([GNULIB_TRUNCF]) GNULIB_TRUNCL=0; AC_SUBST([GNULIB_TRUNCL]) + GNULIB_ISFINITE=0; AC_SUBST([GNULIB_ISFINITE]) dnl Assume proper GNU behavior unless another module says otherwise. HAVE_DECL_ACOSL=1; AC_SUBST([HAVE_DECL_ACOSL]) HAVE_DECL_ASINL=1; AC_SUBST([HAVE_DECL_ASINL]) @@ -56,6 +57,7 @@ AC_DEFUN([gl_MATH_H_DEFAULTS], HAVE_DECL_TRUNC=1; AC_SUBST([HAVE_DECL_TRUNC]) HAVE_DECL_TRUNCF=1; AC_SUBST([HAVE_DECL_TRUNCF]) HAVE_DECL_TRUNCL=1; AC_SUBST([HAVE_DECL_TRUNCL]) + HAVE_DECL_ISFINITE=1; AC_SUBST([HAVE_DECL_ISFINITE]) REPLACE_FREXP=0; AC_SUBST([REPLACE_FREXP]) REPLACE_FREXPL=0; AC_SUBST([REPLACE_FREXPL]) REPLACE_LDEXPL=0; AC_SUBST([REPLACE_LDEXPL]) diff --git a/modules/isfinite b/modules/isfinite new file mode 100644 index 0000000..5ceafb4 --- /dev/null +++ b/modules/isfinite @@ -0,0 +1,27 @@ +Description: +isfinite macro: test for finite value (zero, subnormal, or normal, and not infinite or NaN). + +Files: +m4/isfinite.m4 +m4/check-math-lib.m4 + +Depends-on: +inline +math +extensions +float + +configure.ac: +gl_ISFINITE +gl_MATH_MODULE_INDICATOR([isfinite]) + +Makefile.am: + +Include: +<math.h> + +License: +GPL + +Maintainer: +Ben Pfaff diff --git a/modules/isfinite-tests b/modules/isfinite-tests new file mode 100644 index 0000000..bca2e85 --- /dev/null +++ b/modules/isfinite-tests @@ -0,0 +1,14 @@ +Files: +tests/test-isfinite.c + +Depends-on: + +configure.ac: + +Makefile.am: +TESTS += test-isfinite +check_PROGRAMS += test-isfinite +test_isfinite_LDADD = $(LDADD) @ISFINITE_LIBM@ + +License: +GPL diff --git a/modules/math b/modules/math index 540f2fa..935bebb 100644 --- a/modules/math +++ b/modules/math @@ -37,6 +37,7 @@ math.h: math.in.h -e 's|@''GNULIB_TRUNC''@|$(GNULIB_TRUNC)|g' \ -e 's|@''GNULIB_TRUNCF''@|$(GNULIB_TRUNCF)|g' \ -e 's|@''GNULIB_TRUNCL''@|$(GNULIB_TRUNCL)|g' \ + -e 's|@''GNULIB_ISFINITE''@|$(GNULIB_ISFINITE)|g' \ -e 's|@''HAVE_DECL_ACOSL''@|$(HAVE_DECL_ACOSL)|g' \ -e 's|@''HAVE_DECL_ASINL''@|$(HAVE_DECL_ASINL)|g' \ -e 's|@''HAVE_DECL_ATANL''@|$(HAVE_DECL_ATANL)|g' \ @@ -58,6 +59,7 @@ math.h: math.in.h -e 's|@''HAVE_DECL_TRUNC''@|$(HAVE_DECL_TRUNC)|g' \ -e 's|@''HAVE_DECL_TRUNCF''@|$(HAVE_DECL_TRUNCF)|g' \ -e 's|@''HAVE_DECL_TRUNCL''@|$(HAVE_DECL_TRUNCL)|g' \ + -e 's|@''HAVE_DECL_ISFINITE''@|$(HAVE_DECL_ISFINITE)|g' \ -e 's|@''REPLACE_FREXP''@|$(REPLACE_FREXP)|g' \ -e 's|@''REPLACE_FREXPL''@|$(REPLACE_FREXPL)|g' \ -e 's|@''REPLACE_LDEXPL''@|$(REPLACE_LDEXPL)|g' \ diff --git a/tests/test-isfinite.c b/tests/test-isfinite.c new file mode 100644 index 0000000..43d0ee1 --- /dev/null +++ b/tests/test-isfinite.c @@ -0,0 +1,116 @@ +/* Test of isfinite() substitute. + Copyright (C) 2007 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 <http://www.gnu.org/licenses/>. */ + +/* Written by Ben Pfaff, 2007, using Bruno Haible's code as a + template. */ + +#include <config.h> + +#include <float.h> +#include <math.h> + +#include <stdio.h> +#include <stdlib.h> + +#define ASSERT(expr) \ + do \ + { \ + if (!(expr)) \ + { \ + fprintf (stderr, "%s:%d: assertion failed\n", __FILE__, __LINE__); \ + abort (); \ + } \ + } \ + while (0) + +float zerof = 0.0f; +double zerod = 0.0; +long double zerol = 0.0L; + +static void +test_isfinitef () +{ + /* Zero. */ + ASSERT (isfinite (0.0f)); + /* Subnormal values. */ + ASSERT (isfinite (FLT_MIN / 2)); + ASSERT (isfinite (-FLT_MIN / 2)); + /* Finite values. */ + ASSERT (isfinite (3.141f)); + ASSERT (isfinite (3.141e30f)); + ASSERT (isfinite (3.141e-30f)); + ASSERT (isfinite (-2.718f)); + ASSERT (isfinite (-2.718e30f)); + ASSERT (isfinite (-2.718e-30f)); + /* Infinite values. */ + ASSERT (!isfinite (1.0f / 0.0f)); + ASSERT (!isfinite (-1.0f / 0.0f)); + /* Quiet NaN. */ + ASSERT (!isfinite (zerof / zerof)); +} + +static void +test_isfinited () +{ + /* Zero. */ + ASSERT (isfinite (0.0)); + /* Subnormal values. */ + ASSERT (isfinite (DBL_MIN / 2)); + ASSERT (isfinite (-DBL_MIN / 2)); + /* Finite values. */ + ASSERT (isfinite (3.141)); + ASSERT (isfinite (3.141e30)); + ASSERT (isfinite (3.141e-30)); + ASSERT (isfinite (-2.718)); + ASSERT (isfinite (-2.718e30)); + ASSERT (isfinite (-2.718e-30)); + /* Infinite values. */ + ASSERT (!isfinite (1.0 / 0.0)); + ASSERT (!isfinite (-1.0 / 0.0)); + /* Quiet NaN. */ + ASSERT (!isfinite (zerod / zerod)); +} + +static void +test_isfinitel () +{ + /* Zero. */ + ASSERT (isfinite (0.0L)); + /* Subnormal values. */ + ASSERT (isfinite (LDBL_MIN / 2)); + ASSERT (isfinite (-LDBL_MIN / 2)); + /* Finite values. */ + ASSERT (isfinite (3.141L)); + ASSERT (isfinite (3.141e30L)); + ASSERT (isfinite (3.141e-30L)); + ASSERT (isfinite (-2.718L)); + ASSERT (isfinite (-2.718e30L)); + ASSERT (isfinite (-2.718e-30L)); + /* Infinite values. */ + ASSERT (!isfinite (1.0L / 0.0L)); + ASSERT (!isfinite (-1.0L / 0.0L)); + /* Quiet NaN. */ + ASSERT (!isfinite (zerol / zerol)); +} + +int +main () +{ + test_isfinitef (); + test_isfinited (); + test_isfinitel (); + return 0; +} -- "A computer is a state machine. Threads are for people who cant [sic] program state machines." --Alan Cox