changeset 16751:9d599e388a0f

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.
author Bruno Haible <bruno@clisp.org>
date Tue, 03 Apr 2012 02:51:42 +0200
parents 8b54ed378c86
children 27c536025f33
files ChangeLog doc/posix-functions/logb.texi lib/logb.c lib/math.in.h m4/logb.m4 m4/math_h.m4 modules/logb modules/math tests/test-math-c++.cc
diffstat 9 files changed, 207 insertions(+), 26 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,20 @@
+2012-04-02  Bruno Haible  <bruno@clisp.org>
+
+	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  <bruno@clisp.org>
 
 	log10* tests: Speed up.
--- 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
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 <http://www.gnu.org/licenses/>.  */
+
+#if ! (defined USE_LONG_DOUBLE || defined USE_FLOAT)
+# include <config.h>
+#endif
+
+/* Specification.  */
+#include <math.h>
+
+#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;
+    }
+}
--- 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)
--- 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 <float.h>
+#include <math.h>
+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
+        ])
+    ])
+])
--- 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])
--- 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:
--- 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' \
--- 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