From 77e408383e7944caaf77cb068e670238aa7ee905 Mon Sep 17 00:00:00 2001 From: Bruno Haible Date: Tue, 3 Apr 2012 02:51:42 +0200 Subject: [PATCH] logb: Provide replacement and workarounds. * lib/math.in.h (logb): Ensure declaration. Replace if REPLACE_LOGB is 1. * lib/logb.c: New file. * m4/logb.m4 (gl_FUNC_LOGB_WORKS): New macro. (gl_FUNC_LOGB): Invoke it. Set HAVE_LOGB, REPLACE_LOGB. * m4/math_h.m4 (gl_MATH_H_DEFAULTS): Initialize REPLACE_LOGB. * modules/math (Makefile.am): Substitute REPLACE_LOGB. * modules/logb (Files): Add lib/logb.c. (Depends-on): Add isfinite, frexp, isnand. (configure.ac): Compile the replacement code logb.c if needed. * tests/test-math-c++.cc: Check the declaration of logb. * doc/posix-functions/logb.texi: Mention the replacement and the bug with subnormal numbers. --- ChangeLog | 17 +++++++++++ doc/posix-functions/logb.texi | 9 ++++-- lib/logb.c | 71 +++++++++++++++++++++++++++++++++++++++++++ lib/math.in.h | 37 ++++++++++++++-------- m4/logb.m4 | 71 +++++++++++++++++++++++++++++++++++++++++-- m4/math_h.m4 | 10 +++--- modules/logb | 7 +++++ modules/math | 5 +-- tests/test-math-c++.cc | 6 ++-- 9 files changed, 207 insertions(+), 26 deletions(-) create mode 100644 lib/logb.c diff --git a/ChangeLog b/ChangeLog index 729e245cc..b4c162d21 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,22 @@ 2012-04-02 Bruno Haible + logb: Provide replacement and workarounds. + * lib/math.in.h (logb): Ensure declaration. Replace if REPLACE_LOGB + is 1. + * lib/logb.c: New file. + * m4/logb.m4 (gl_FUNC_LOGB_WORKS): New macro. + (gl_FUNC_LOGB): Invoke it. Set HAVE_LOGB, REPLACE_LOGB. + * m4/math_h.m4 (gl_MATH_H_DEFAULTS): Initialize REPLACE_LOGB. + * modules/math (Makefile.am): Substitute REPLACE_LOGB. + * modules/logb (Files): Add lib/logb.c. + (Depends-on): Add isfinite, frexp, isnand. + (configure.ac): Compile the replacement code logb.c if needed. + * tests/test-math-c++.cc: Check the declaration of logb. + * doc/posix-functions/logb.texi: Mention the replacement and the bug + with subnormal numbers. + +2012-04-02 Bruno Haible + log10* tests: Speed up. * tests/test-log10.h (test_function): Reduce amount of random numbers to test. diff --git a/doc/posix-functions/logb.texi b/doc/posix-functions/logb.texi index 3ea333a8b..799d9a984 100644 --- a/doc/posix-functions/logb.texi +++ b/doc/posix-functions/logb.texi @@ -9,13 +9,16 @@ Gnulib module: logb Portability problems fixed by Gnulib: @itemize @item +This function is missing on some platforms: +Minix 3.1.8, MSVC 9. +@item This function is missing a declaration on some platforms: Cygwin 1.5.x. +@item +This function produces wrong results for subnormal numbers on some platforms: +glibc 2.11/ppc, glibc 2.7/sparc, glibc 2.7/hppa, Solaris 11 2011-11, Cygwin 1.5.x. @end itemize Portability problems not fixed by Gnulib: @itemize -@item -This function is missing on some platforms: -Minix 3.1.8, MSVC 9. @end itemize diff --git a/lib/logb.c b/lib/logb.c new file mode 100644 index 000000000..d56be557a --- /dev/null +++ b/lib/logb.c @@ -0,0 +1,71 @@ +/* Floating-point exponent. + Copyright (C) 2012 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 . */ + +#if ! (defined USE_LONG_DOUBLE || defined USE_FLOAT) +# include +#endif + +/* Specification. */ +#include + +#ifdef USE_LONG_DOUBLE +# define LOGB logbl +# define DOUBLE long double +# define L_(literal) literal##L +# define HUGEVAL HUGE_VALL +# define FREXP frexpl +# define ISNAN isnanl +#elif ! defined USE_FLOAT +# define LOGB logb +# define DOUBLE double +# define L_(literal) literal +# define HUGEVAL HUGE_VAL +# define FREXP frexp +# define ISNAN isnand +#else /* defined USE_FLOAT */ +# define LOGB logbf +# define DOUBLE float +# define L_(literal) literal##f +# define HUGEVAL HUGE_VALF +# define FREXP frexpf +# define ISNAN isnanf +#endif + +DOUBLE +LOGB (DOUBLE x) +{ + if (isfinite (x)) + { + if (x == L_(0.0)) + /* Return -Infinity. */ + return - HUGEVAL; + else + { + int e; + + (void) FREXP (x, &e); + return (DOUBLE) (e - 1); + } + } + else + { + if (ISNAN (x)) + return x; /* NaN */ + else + /* Return +Infinity. */ + return HUGEVAL; + } +} diff --git a/lib/math.in.h b/lib/math.in.h index f5c5f0391..40983a964 100644 --- a/lib/math.in.h +++ b/lib/math.in.h @@ -1108,19 +1108,6 @@ _GL_WARN_ON_USE (ldexpl, "ldexpl is unportable - " #endif -#if @GNULIB_LOGB@ -# if !@HAVE_DECL_LOGB@ -_GL_EXTERN_C double logb (double x); -# endif -#elif defined GNULIB_POSIXCHECK -# undef logb -# if HAVE_RAW_DECL_LOGB -_GL_WARN_ON_USE (logb, "logb is unportable - " - "use gnulib module logb for portability"); -# endif -#endif - - #if @GNULIB_LOGF@ # if @REPLACE_LOGF@ # if !(defined __cplusplus && defined GNULIB_NAMESPACE) @@ -1401,6 +1388,30 @@ _GL_WARN_ON_USE (log2l, "log2l is unportable - " #endif +#if @GNULIB_LOGB@ +# if @REPLACE_LOGB@ +# if !(defined __cplusplus && defined GNULIB_NAMESPACE) +# undef logb +# define logb rpl_logb +# endif +_GL_FUNCDECL_RPL (logb, double, (double x)); +_GL_CXXALIAS_RPL (logb, double, (double x)); +# else +# if !@HAVE_DECL_LOGB@ +_GL_FUNCDECL_SYS (logb, double, (double x)); +# endif +_GL_CXXALIAS_SYS (logb, double, (double x)); +# endif +_GL_CXXALIASWARN (logb); +#elif defined GNULIB_POSIXCHECK +# undef logb +# if HAVE_RAW_DECL_LOGB +_GL_WARN_ON_USE (logb, "logb is unportable - " + "use gnulib module logb for portability"); +# endif +#endif + + #if @GNULIB_MODFF@ # if @REPLACE_MODFF@ # if !(defined __cplusplus && defined GNULIB_NAMESPACE) diff --git a/m4/logb.m4 b/m4/logb.m4 index ee23e243d..66c9e2a77 100644 --- a/m4/logb.m4 +++ b/m4/logb.m4 @@ -1,4 +1,4 @@ -# logb.m4 serial 5 +# logb.m4 serial 6 dnl Copyright (C) 2010-2012 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, @@ -51,8 +51,75 @@ AC_DEFUN([gl_FUNC_LOGB], [LOGB_LIBM="-lm"]) LIBS="$save_LIBS" fi - if test "$LOGB_LIBM" = "?"; then + if test "$LOGB_LIBM" != "?"; then + HAVE_LOGB=1 + save_LIBS="$LIBS" + LIBS="$LIBS $LOGB_LIBM" + gl_FUNC_LOGB_WORKS + LIBS="$save_LIBS" + case "$gl_cv_func_logb_works" in + *yes) ;; + *) REPLACE_LOGB=1 ;; + esac + else + HAVE_LOGB=0 + fi + if test $HAVE_LOGB = 0 || test $REPLACE_LOGB = 1; then + dnl Find libraries needed to link lib/logb.c. + AC_REQUIRE([gl_FUNC_FREXP]) + AC_REQUIRE([gl_FUNC_ISNAND]) LOGB_LIBM= + dnl Append $FREXP_LIBM to LOGB_LIBM, avoiding gratuitous duplicates. + case " $LOGB_LIBM " in + *" $FREXP_LIBM "*) ;; + *) LOGB_LIBM="$LOGB_LIBM $FREXP_LIBM" ;; + esac + dnl Append $ISNAND_LIBM to LOGB_LIBM, avoiding gratuitous duplicates. + case " $LOGB_LIBM " in + *" $ISNAND_LIBM "*) ;; + *) LOGB_LIBM="$LOGB_LIBM $ISNAND_LIBM" ;; + esac fi AC_SUBST([LOGB_LIBM]) ]) + +dnl Test whether logb() works. +dnl On glibc 2.11/ppc, glibc 2.7/sparc, glibc 2.7/hppa, Solaris 10/SPARC, +dnl Cygwin 1.5.x, the return value for subnormal (denormalized) arguments is +dnl too large. +AC_DEFUN([gl_FUNC_LOGB_WORKS], +[ + AC_REQUIRE([AC_PROG_CC]) + AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles + AC_CACHE_CHECK([whether logb works], [gl_cv_func_logb_works], + [ + AC_RUN_IFELSE( + [AC_LANG_SOURCE([[ +#include +#include +extern +#ifdef __cplusplus +"C" +#endif +double logb (double); +volatile double x; +int main () +{ + int i; + for (i = 1, x = 1.0; i >= DBL_MIN_EXP; i--, x *= 0.5) + ; + /* Here i = DBL_MIN_EXP - 1. Either x = 2^(i-1) is subnormal or x = 0.0. */ + if (x > 0.0 && !(logb (x) == (double)(i - 1))) + return 1; + return 0; +} +]])], + [gl_cv_func_logb_works=yes], + [gl_cv_func_logb_works=no], + [case "$host_os" in + *gnu* | solaris* | cygwin*) gl_cv_func_logb_works="guessing no";; + *) gl_cv_func_logb_works="guessing yes";; + esac + ]) + ]) +]) diff --git a/m4/math_h.m4 b/m4/math_h.m4 index a24d35289..fe0cd0efa 100644 --- a/m4/math_h.m4 +++ b/m4/math_h.m4 @@ -1,4 +1,4 @@ -# math_h.m4 serial 107 +# math_h.m4 serial 108 dnl Copyright (C) 2007-2012 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, @@ -45,7 +45,8 @@ AC_DEFUN([gl_MATH_H], fabsf fabsl floorf floorl fma fmaf fmal fmod fmodf fmodl frexpf frexpl hypotf hypotl ldexpf ldexpl - logb log logf logl log10 log10f log10l log1p log1pf log1pl log2 log2f log2l + log logf logl log10 log10f log10l log1p log1pf log1pl log2 log2f log2l + logb modf modff modfl powf remainder remainderf remainderl rint rintf rintl round roundf roundl sinf sinl sinhf sqrtf sqrtl @@ -115,7 +116,6 @@ AC_DEFUN([gl_MATH_H_DEFAULTS], GNULIB_ISNANL=0; AC_SUBST([GNULIB_ISNANL]) GNULIB_LDEXPF=0; AC_SUBST([GNULIB_LDEXPF]) GNULIB_LDEXPL=0; AC_SUBST([GNULIB_LDEXPL]) - GNULIB_LOGB=0; AC_SUBST([GNULIB_LOGB]) GNULIB_LOG=0; AC_SUBST([GNULIB_LOG]) GNULIB_LOGF=0; AC_SUBST([GNULIB_LOGF]) GNULIB_LOGL=0; AC_SUBST([GNULIB_LOGL]) @@ -128,6 +128,7 @@ AC_DEFUN([gl_MATH_H_DEFAULTS], GNULIB_LOG2=0; AC_SUBST([GNULIB_LOG2]) GNULIB_LOG2F=0; AC_SUBST([GNULIB_LOG2F]) GNULIB_LOG2L=0; AC_SUBST([GNULIB_LOG2L]) + GNULIB_LOGB=0; AC_SUBST([GNULIB_LOGB]) GNULIB_MODF=0; AC_SUBST([GNULIB_MODF]) GNULIB_MODFF=0; AC_SUBST([GNULIB_MODFF]) GNULIB_MODFL=0; AC_SUBST([GNULIB_MODFL]) @@ -227,12 +228,12 @@ AC_DEFUN([gl_MATH_H_DEFAULTS], HAVE_DECL_FLOORL=1; AC_SUBST([HAVE_DECL_FLOORL]) HAVE_DECL_FREXPL=1; AC_SUBST([HAVE_DECL_FREXPL]) HAVE_DECL_LDEXPL=1; AC_SUBST([HAVE_DECL_LDEXPL]) - HAVE_DECL_LOGB=1; AC_SUBST([HAVE_DECL_LOGB]) HAVE_DECL_LOGL=1; AC_SUBST([HAVE_DECL_LOGL]) HAVE_DECL_LOG10L=1; AC_SUBST([HAVE_DECL_LOG10L]) HAVE_DECL_LOG2=1; AC_SUBST([HAVE_DECL_LOG2]) HAVE_DECL_LOG2F=1; AC_SUBST([HAVE_DECL_LOG2F]) HAVE_DECL_LOG2L=1; AC_SUBST([HAVE_DECL_LOG2L]) + HAVE_DECL_LOGB=1; AC_SUBST([HAVE_DECL_LOGB]) HAVE_DECL_REMAINDER=1; AC_SUBST([HAVE_DECL_REMAINDER]) HAVE_DECL_REMAINDERL=1; AC_SUBST([HAVE_DECL_REMAINDERL]) HAVE_DECL_RINTF=1; AC_SUBST([HAVE_DECL_RINTF]) @@ -287,6 +288,7 @@ AC_DEFUN([gl_MATH_H_DEFAULTS], REPLACE_LOG2=0; AC_SUBST([REPLACE_LOG2]) REPLACE_LOG2F=0; AC_SUBST([REPLACE_LOG2F]) REPLACE_LOG2L=0; AC_SUBST([REPLACE_LOG2L]) + REPLACE_LOGB=0; AC_SUBST([REPLACE_LOGB]) REPLACE_MODF=0; AC_SUBST([REPLACE_MODF]) REPLACE_MODFF=0; AC_SUBST([REPLACE_MODFF]) REPLACE_MODFL=0; AC_SUBST([REPLACE_MODFL]) diff --git a/modules/logb b/modules/logb index 9747473aa..91df8a4eb 100644 --- a/modules/logb +++ b/modules/logb @@ -2,15 +2,22 @@ Description: logb() function: get exponent. Files: +lib/logb.c m4/logb.m4 m4/mathfunc.m4 Depends-on: math extensions +isfinite [test $HAVE_LOGB = 0 || test $REPLACE_LOGB = 1] +frexp [test $HAVE_LOGB = 0 || test $REPLACE_LOGB = 1] +isnand [test $HAVE_LOGB = 0 || test $REPLACE_LOGB = 1] configure.ac: gl_FUNC_LOGB +if test $HAVE_LOGB = 0 || test $REPLACE_LOGB = 1; then + AC_LIBOBJ([logb]) +fi gl_MATH_MODULE_INDICATOR([logb]) Makefile.am: diff --git a/modules/math b/modules/math index 68fe9586f..26a80b0c0 100644 --- a/modules/math +++ b/modules/math @@ -80,7 +80,6 @@ math.h: math.in.h $(top_builddir)/config.status $(CXXDEFS_H) $(ARG_NONNULL_H) $( -e 's/@''GNULIB_ISNANL''@/$(GNULIB_ISNANL)/g' \ -e 's/@''GNULIB_LDEXPF''@/$(GNULIB_LDEXPF)/g' \ -e 's/@''GNULIB_LDEXPL''@/$(GNULIB_LDEXPL)/g' \ - -e 's/@''GNULIB_LOGB''@/$(GNULIB_LOGB)/g' \ -e 's/@''GNULIB_LOG''@/$(GNULIB_LOG)/g' \ -e 's/@''GNULIB_LOGF''@/$(GNULIB_LOGF)/g' \ -e 's/@''GNULIB_LOGL''@/$(GNULIB_LOGL)/g' \ @@ -93,6 +92,7 @@ math.h: math.in.h $(top_builddir)/config.status $(CXXDEFS_H) $(ARG_NONNULL_H) $( -e 's/@''GNULIB_LOG2''@/$(GNULIB_LOG2)/g' \ -e 's/@''GNULIB_LOG2F''@/$(GNULIB_LOG2F)/g' \ -e 's/@''GNULIB_LOG2L''@/$(GNULIB_LOG2L)/g' \ + -e 's/@''GNULIB_LOGB''@/$(GNULIB_LOGB)/g' \ -e 's/@''GNULIB_MODF''@/$(GNULIB_MODF)/g' \ -e 's/@''GNULIB_MODFF''@/$(GNULIB_MODFF)/g' \ -e 's/@''GNULIB_MODFL''@/$(GNULIB_MODFL)/g' \ @@ -192,12 +192,12 @@ math.h: math.in.h $(top_builddir)/config.status $(CXXDEFS_H) $(ARG_NONNULL_H) $( -e 's|@''HAVE_DECL_FLOORL''@|$(HAVE_DECL_FLOORL)|g' \ -e 's|@''HAVE_DECL_FREXPL''@|$(HAVE_DECL_FREXPL)|g' \ -e 's|@''HAVE_DECL_LDEXPL''@|$(HAVE_DECL_LDEXPL)|g' \ - -e 's|@''HAVE_DECL_LOGB''@|$(HAVE_DECL_LOGB)|g' \ -e 's|@''HAVE_DECL_LOGL''@|$(HAVE_DECL_LOGL)|g' \ -e 's|@''HAVE_DECL_LOG10L''@|$(HAVE_DECL_LOG10L)|g' \ -e 's|@''HAVE_DECL_LOG2''@|$(HAVE_DECL_LOG2)|g' \ -e 's|@''HAVE_DECL_LOG2F''@|$(HAVE_DECL_LOG2F)|g' \ -e 's|@''HAVE_DECL_LOG2L''@|$(HAVE_DECL_LOG2L)|g' \ + -e 's|@''HAVE_DECL_LOGB''@|$(HAVE_DECL_LOGB)|g' \ -e 's|@''HAVE_DECL_REMAINDER''@|$(HAVE_DECL_REMAINDER)|g' \ -e 's|@''HAVE_DECL_REMAINDERL''@|$(HAVE_DECL_REMAINDERL)|g' \ -e 's|@''HAVE_DECL_RINTF''@|$(HAVE_DECL_RINTF)|g' \ @@ -254,6 +254,7 @@ math.h: math.in.h $(top_builddir)/config.status $(CXXDEFS_H) $(ARG_NONNULL_H) $( -e 's|@''REPLACE_LOG2''@|$(REPLACE_LOG2)|g' \ -e 's|@''REPLACE_LOG2F''@|$(REPLACE_LOG2F)|g' \ -e 's|@''REPLACE_LOG2L''@|$(REPLACE_LOG2L)|g' \ + -e 's|@''REPLACE_LOGB''@|$(REPLACE_LOGB)|g' \ -e 's|@''REPLACE_MODF''@|$(REPLACE_MODF)|g' \ -e 's|@''REPLACE_MODFF''@|$(REPLACE_MODFF)|g' \ -e 's|@''REPLACE_MODFL''@|$(REPLACE_MODFL)|g' \ diff --git a/tests/test-math-c++.cc b/tests/test-math-c++.cc index b6df6595c..492e54a03 100644 --- a/tests/test-math-c++.cc +++ b/tests/test-math-c++.cc @@ -227,8 +227,6 @@ SIGNATURE_CHECK (GNULIB_NAMESPACE::log1p, double, (double)); SIGNATURE_CHECK (GNULIB_NAMESPACE::log1pl, long double, (long double)); #endif -//SIGNATURE_CHECK (GNULIB_NAMESPACE::logb, double, (double)); - #if GNULIB_TEST_LOGF SIGNATURE_CHECK (GNULIB_NAMESPACE::logf, float, (float)); #endif @@ -253,6 +251,10 @@ SIGNATURE_CHECK (GNULIB_NAMESPACE::log2, double, (double)); SIGNATURE_CHECK (GNULIB_NAMESPACE::log2l, long double, (long double)); #endif +#if GNULIB_TEST_LOGB +SIGNATURE_CHECK (GNULIB_NAMESPACE::logb, double, (double)); +#endif + #if GNULIB_TEST_MODFF SIGNATURE_CHECK (GNULIB_NAMESPACE::modff, float, (float, float *)); #endif -- 2.11.0