* lib/math.in.h (ldexp): New declaration.
* lib/ldexp.c: New file, based on lib/ldexpl.c.
* lib/ldexpl.c: Moved the implementation to lib/ldexp.c. Just include
it.
* m4/math_h.m4 (gl_MATH_H_REQUIRE_DEFAULTS): Initialize GNULIB_LDEXP.
(gl_MATH_H_DEFAULTS): Initialize REPLACE_LDEXP.
* m4/ldexp.m4 (gl_FUNC_LDEXP): Require gl_MATH_H_DEFAULTS and
gl_FUNC_ISNAND. Invoke gl_FUNC_LDEXP_WORKS. Set REPLACE_LDEXP. Consider
it when setting LDEXP_LIBM.
(gl_FUNC_LDEXP_WORKS): New macro.
* modules/math (Makefile.am): Substitute GNULIB_LDEXP, REPLACE_LDEXP.
* modules/ldexp (Files): Add lib/ldexp.c.
(Depends-on): Add math, isnand.
(configure.ac): Set GL_COND_OBJ_LDEXP. Invoke gl_MATH_MODULE_INDICATOR.
(Makefile.am): Conditionally compile ldexp.c.
* modules/ldexpl (Files): Add lib/ldexp.c.
* doc/posix-functions/ldexp.texi: Mention the OpenBSD bug.
+2023-08-19 Bruno Haible <bruno@clisp.org>
+
+ ldexp: Work around OpenBSD/mips64 bug.
+ * lib/math.in.h (ldexp): New declaration.
+ * lib/ldexp.c: New file, based on lib/ldexpl.c.
+ * lib/ldexpl.c: Moved the implementation to lib/ldexp.c. Just include
+ it.
+ * m4/math_h.m4 (gl_MATH_H_REQUIRE_DEFAULTS): Initialize GNULIB_LDEXP.
+ (gl_MATH_H_DEFAULTS): Initialize REPLACE_LDEXP.
+ * m4/ldexp.m4 (gl_FUNC_LDEXP): Require gl_MATH_H_DEFAULTS and
+ gl_FUNC_ISNAND. Invoke gl_FUNC_LDEXP_WORKS. Set REPLACE_LDEXP. Consider
+ it when setting LDEXP_LIBM.
+ (gl_FUNC_LDEXP_WORKS): New macro.
+ * modules/math (Makefile.am): Substitute GNULIB_LDEXP, REPLACE_LDEXP.
+ * modules/ldexp (Files): Add lib/ldexp.c.
+ (Depends-on): Add math, isnand.
+ (configure.ac): Set GL_COND_OBJ_LDEXP. Invoke gl_MATH_MODULE_INDICATOR.
+ (Makefile.am): Conditionally compile ldexp.c.
+ * modules/ldexpl (Files): Add lib/ldexp.c.
+ * doc/posix-functions/ldexp.texi: Mention the OpenBSD bug.
+
2023-08-19 Bruno Haible <bruno@clisp.org>
ldexpl: Relicense under LGPLv2+.
Portability problems fixed by Gnulib:
@itemize
+@item
+This function produces wrong results on some platforms:
+@c ldexp(1.9269695883136991774e-308,0) returns 7.32274e-245.
+OpenBSD 7.3/mips64.
@end itemize
Portability problems not fixed by Gnulib:
--- /dev/null
+/* Multiply a 'float' by a power of 2.
+ Copyright 2002-2003, 2007-2023 Free Software Foundation, Inc.
+
+ This file is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of the
+ License, or (at your option) any later version.
+
+ This file 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 Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>. */
+
+/* Written by Paolo Bonzini and Bruno Haible. */
+
+#include <config.h>
+
+/* Specification. */
+#include <math.h>
+
+# include <float.h>
+#ifdef USE_LONG_DOUBLE
+# include "fpucw.h"
+#endif
+
+/* Avoid some warnings from "gcc -Wshadow".
+ This file doesn't use the exp() function. */
+#undef exp
+#define exp exponent
+
+#ifdef USE_LONG_DOUBLE
+# define FUNC ldexpl
+# define DOUBLE long double
+# define ISNAN isnanl
+# define DECL_ROUNDING DECL_LONG_DOUBLE_ROUNDING
+# define BEGIN_ROUNDING() BEGIN_LONG_DOUBLE_ROUNDING ()
+# define END_ROUNDING() END_LONG_DOUBLE_ROUNDING ()
+# define L_(literal) literal##L
+#else
+# define FUNC ldexp
+# define DOUBLE double
+# define ISNAN isnand
+# define DECL_ROUNDING
+# define BEGIN_ROUNDING()
+# define END_ROUNDING()
+# define L_(literal) literal
+#endif
+
+DOUBLE
+FUNC (DOUBLE x, int exp)
+{
+ DECL_ROUNDING
+
+ BEGIN_ROUNDING ();
+
+ /* Check for zero, nan and infinity. */
+ if (!(ISNAN (x) || x + x == x))
+ {
+ unsigned int uexp;
+ DOUBLE factor;
+
+ if (exp < 0)
+ {
+ /* Avoid signed integer overflow when exp == INT_MIN. */
+ uexp = (unsigned int) (-1 - exp) + 1;
+ factor = L_(0.5);
+ }
+ else
+ {
+ uexp = exp;
+ factor = L_(2.0);
+ }
+
+ if (uexp > 0)
+ {
+ unsigned int bit;
+
+ for (bit = 1;;)
+ {
+ /* Invariant: Here bit = 2^i, factor = 2^-2^i or = 2^2^i,
+ and bit <= uexp. */
+ if (uexp & bit)
+ x *= factor;
+ bit <<= 1;
+ if (bit == 0 || bit > uexp)
+ break;
+ factor = factor * factor;
+ }
+ }
+ }
+
+ END_ROUNDING ();
+
+ return x;
+}
-/* Emulation for ldexpl.
- Contributed by Paolo Bonzini
-
+/* Multiply a 'float' by a power of 2.
Copyright 2002-2003, 2007-2023 Free Software Foundation, Inc.
- This file is part of gnulib.
-
This file is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of the
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. */
+/* Written by Paolo Bonzini and Bruno Haible. */
+
#include <config.h>
/* Specification. */
#else
-# include <float.h>
-# include "fpucw.h"
-
-long double
-ldexpl (long double x, int exp)
-{
- unsigned int uexp;
- long double factor;
- unsigned int bit;
- DECL_LONG_DOUBLE_ROUNDING
-
- BEGIN_LONG_DOUBLE_ROUNDING ();
-
- /* Check for zero, nan and infinity. */
- if (!(isnanl (x) || x + x == x))
- {
- if (exp < 0)
- {
- /* Avoid signed integer overflow when exp == INT_MIN. */
- uexp = (unsigned int) (-1 - exp) + 1;
- factor = 0.5L;
- }
- else
- {
- uexp = exp;
- factor = 2.0L;
- }
-
- if (uexp > 0)
- for (bit = 1;;)
- {
- /* Invariant: Here bit = 2^i, factor = 2^-2^i or = 2^2^i,
- and bit <= uexp. */
- if (uexp & bit)
- x *= factor;
- bit <<= 1;
- if (bit == 0 || bit > uexp)
- break;
- factor = factor * factor;
- }
- }
-
- END_LONG_DOUBLE_ROUNDING ();
-
- return x;
-}
+# define USE_LONG_DOUBLE
+# include "ldexp.c"
#endif
# endif
#endif
+/* Return x * 2^exp. */
+#if @GNULIB_LDEXP@
+# if @REPLACE_LDEXP@
+# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+# undef ldexp
+# define ldexp rpl_ldexp
+# endif
+_GL_FUNCDECL_RPL (ldexp, double, (double x, int exp));
+_GL_CXXALIAS_RPL (ldexp, double, (double x, int exp));
+# else
+/* Assume ldexp is always declared. */
+_GL_CXXALIAS_SYS (ldexp, double, (double x, int exp));
+# endif
+# if __GLIBC__ >= 2
+_GL_CXXALIASWARN (ldexp);
+# endif
+#elif defined GNULIB_POSIXCHECK
+# undef ldexp
+/* Assume ldexp is always declared. */
+_GL_WARN_ON_USE (ldexp, "ldexp is unportable - "
+ "use gnulib module ldexp for portability");
+#endif
+
/* Return x * 2^exp. */
#if @GNULIB_LDEXPL@ && @REPLACE_LDEXPL@
# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
-# ldexp.m4 serial 1
+# ldexp.m4 serial 2
dnl Copyright (C) 2010-2023 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
AC_DEFUN([gl_FUNC_LDEXP],
[
+ AC_REQUIRE([gl_MATH_H_DEFAULTS])
+ AC_REQUIRE([gl_FUNC_ISNAND]) dnl for ISNAND_LIBM
+
AC_REQUIRE([gl_CHECK_LDEXP_NO_LIBM])
LDEXP_LIBM=
if test $gl_cv_func_ldexp_no_libm = no; then
LDEXP_LIBM=-lm
fi
fi
+
+ save_LIBS="$LIBS"
+ LIBS="$LIBS $LDEXP_LIBM"
+ gl_FUNC_LDEXP_WORKS
+ LIBS="$save_LIBS"
+ case "$gl_cv_func_ldexp_works" in
+ *yes) ;;
+ *) REPLACE_LDEXP=1 ;;
+ esac
+
+ if test $REPLACE_LDEXP = 1; then
+ dnl Find libraries needed to link lib/ldexp.c.
+ LDEXP_LIBM="$ISNAND_LIBM"
+ fi
AC_SUBST([LDEXP_LIBM])
])
[gl_cv_func_ldexp_no_libm=no])
])
])
+
+dnl Test whether ldexp() works (this fails on OpenBSD 7.3/mips64).
+AC_DEFUN([gl_FUNC_LDEXP_WORKS],
+[
+ AC_REQUIRE([AC_PROG_CC])
+ AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles
+ AC_CACHE_CHECK([whether ldexp works], [gl_cv_func_ldexp_works],
+ [
+ AC_RUN_IFELSE(
+ [AC_LANG_SOURCE([[
+#include <math.h>
+int main()
+{
+ int result = 0;
+ {
+ volatile double x = 1.9269695883136991774e-308;
+ volatile double y = ldexp (x, 0);
+ if (y != x)
+ result |= 1;
+ }
+ return result;
+}]])],
+ [gl_cv_func_ldexp_works=yes],
+ [gl_cv_func_ldexp_works=no],
+ [case "$host_os" in
+ openbsd*) gl_cv_func_ldexp_works="guessing no" ;;
+ # Guess yes on native Windows.
+ mingw* | windows*) gl_cv_func_ldexp_works="guessing yes" ;;
+ *) gl_cv_func_ldexp_works="guessing yes" ;;
+ esac
+ ])
+ ])
+])
-# math_h.m4 serial 125
+# math_h.m4 serial 126
dnl Copyright (C) 2007-2023 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_ISNANF])
gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_ISNAND])
gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_ISNANL])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_LDEXP])
gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_LDEXPF])
gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_LDEXPL])
gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_LOG])
REPLACE_ISFINITE=0; AC_SUBST([REPLACE_ISFINITE])
REPLACE_ISINF=0; AC_SUBST([REPLACE_ISINF])
REPLACE_ISNAN=0; AC_SUBST([REPLACE_ISNAN])
+ REPLACE_LDEXP=0; AC_SUBST([REPLACE_LDEXP])
REPLACE_LDEXPL=0; AC_SUBST([REPLACE_LDEXPL])
REPLACE_LOG=0; AC_SUBST([REPLACE_LOG])
REPLACE_LOGF=0; AC_SUBST([REPLACE_LOGF])
ldexp() function: multiply a 'double' by a power of 2.
Files:
+lib/ldexp.c
m4/ldexp.m4
Depends-on:
+math
+isnand [test $REPLACE_LDEXP = 1]
configure.ac:
gl_FUNC_LDEXP
+gl_CONDITIONAL([GL_COND_OBJ_LDEXP], [test $REPLACE_LDEXP = 1])
+gl_MATH_MODULE_INDICATOR([ldexp])
Makefile.am:
+if GL_COND_OBJ_LDEXP
+lib_SOURCES += ldexp.c
+endif
Include:
<math.h>
Files:
lib/ldexpl.c
+lib/ldexp.c
m4/ldexpl.m4
Depends-on:
-e 's/@''GNULIB_ISNANF''@/$(GNULIB_ISNANF)/g' \
-e 's/@''GNULIB_ISNAND''@/$(GNULIB_ISNAND)/g' \
-e 's/@''GNULIB_ISNANL''@/$(GNULIB_ISNANL)/g' \
+ -e 's/@''GNULIB_LDEXP''@/$(GNULIB_LDEXP)/g' \
-e 's/@''GNULIB_LDEXPF''@/$(GNULIB_LDEXPF)/g' \
-e 's/@''GNULIB_LDEXPL''@/$(GNULIB_LDEXPL)/g' \
-e 's/@''GNULIB_LOG''@/$(GNULIB_LOG)/g' \
-e 's|@''REPLACE_ITOLD''@|$(REPLACE_ITOLD)|g' \
< $@-t4 > $@-t5
$(AM_V_at)sed \
+ -e 's|@''REPLACE_LDEXP''@|$(REPLACE_LDEXP)|g' \
-e 's|@''REPLACE_LDEXPL''@|$(REPLACE_LDEXPL)|g' \
-e 's|@''REPLACE_LOG''@|$(REPLACE_LOG)|g' \
-e 's|@''REPLACE_LOGF''@|$(REPLACE_LOGF)|g' \