# HG changeset patch # User Bruno Haible # Date 1333414302 -7200 # Node ID 9d599e388a0f3186f8b386b29e8d041cd1d7f996 # Parent 8b54ed378c864238af7402df4a60ec7feb44ddd8 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. diff --git a/ChangeLog b/ChangeLog --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,20 @@ +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. diff --git a/doc/posix-functions/logb.texi b/doc/posix-functions/logb.texi --- a/doc/posix-functions/logb.texi +++ b/doc/posix-functions/logb.texi @@ -9,13 +9,16 @@ 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 --- /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 --- a/lib/math.in.h +++ b/lib/math.in.h @@ -1108,19 +1108,6 @@ #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 @@ #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 --- 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 @@ [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 --- 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 --- a/modules/logb +++ b/modules/logb @@ -2,15 +2,22 @@ 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 --- a/modules/math +++ b/modules/math @@ -80,7 +80,6 @@ -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 @@ -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 @@ -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 @@ -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 --- a/tests/test-math-c++.cc +++ b/tests/test-math-c++.cc @@ -227,8 +227,6 @@ 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::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