* lib/stdlib.in.h (strtold): New declaration.
* lib/strtold.c: New file.
* lib/strtod.c: Consider USE_LONG_DOUBLE.
(STRTOD, LDEXP, HAVE_UNDERLYING_STRTOD, DOUBLE, MIN, MAX, L_,
USE_LDEXP): New macros.
(LDEXP, scale_radix_exp, parse_number, STRTOD): Adapt for
USE_LONG_DOUBLE.
(underlying_strtod): Remove function. Replace with some macros.
Re-add the code for a missing underlying function that was removed on
2013-02-19.
* m4/strtold.m4: New file.
* m4/stdlib_h.m4 (gl_STDLIB_H): Test whether strtold is declared.
(gl_STDLIB_H_DEFAULTS): Initialize GNULIB_STRTOLD, HAVE_STRTOLD,
REPLACE_STRTOLD.
* modules/stdlib (Makefile.am): Substitute GNULIB_STRTOLD, HAVE_STRTOLD,
REPLACE_STRTOLD.
* modules/strtold: New file.
* doc/posix-functions/strtold.texi: Document the new module.
+2019-01-29 Bruno Haible <bruno@clisp.org>
+
+ strtold: New module.
+ * lib/stdlib.in.h (strtold): New declaration.
+ * lib/strtold.c: New file.
+ * lib/strtod.c: Consider USE_LONG_DOUBLE.
+ (STRTOD, LDEXP, HAVE_UNDERLYING_STRTOD, DOUBLE, MIN, MAX, L_,
+ USE_LDEXP): New macros.
+ (LDEXP, scale_radix_exp, parse_number, STRTOD): Adapt for
+ USE_LONG_DOUBLE.
+ (underlying_strtod): Remove function. Replace with some macros.
+ Re-add the code for a missing underlying function that was removed on
+ 2013-02-19.
+ * m4/strtold.m4: New file.
+ * m4/stdlib_h.m4 (gl_STDLIB_H): Test whether strtold is declared.
+ (gl_STDLIB_H_DEFAULTS): Initialize GNULIB_STRTOLD, HAVE_STRTOLD,
+ REPLACE_STRTOLD.
+ * modules/stdlib (Makefile.am): Substitute GNULIB_STRTOLD, HAVE_STRTOLD,
+ REPLACE_STRTOLD.
+ * modules/strtold: New file.
+ * doc/posix-functions/strtold.texi: Document the new module.
+
2019-01-29 Bruno Haible <bruno@clisp.org>
strtod: Fix compilation error on IRIX 6.5.
POSIX specification:@* @url{http://www.opengroup.org/onlinepubs/9699919799/functions/strtold.html}
-Gnulib module: ---
+Gnulib module: strtold
Portability problems fixed by Gnulib:
@itemize
+@item
+This function is missing on some platforms:
+NetBSD 3.0, OpenBSD 3.8, Minix 3.1.8, older IRIX 6.5, OSF/1 4.0, Solaris 9, older Cygwin, MSVC 9, Interix 3.5, BeOS, Android 4.4.
+
+@item
+This function returns a struct, not a @code{long double}, on some platforms:
+HP-UX 11.31/hppa.
+
+@item
+This function consumes whitespace even when there is nothing that should
+be parsed on some platforms:
+IRIX 6.5.
+
+@item
+This function allows whitespace between @samp{e} and the exponent on
+some platforms:
+HP-UX 11.31/ia64, IRIX 6.5.
+
+@item
+This function returns the wrong end pointer for @samp{-0x} on some
+platforms:
+glibc-2.3.2, Mac OS X 10.5, Haiku.
+
+@item
+This function returns +0.0 (not @minus{}0.0) for @samp{-0} on some platforms:
+IRIX 6.5.
+
+@item
+This function fails to parse Infinities and plain NaNs on some platforms:
+HP-UX 11.31/ia64, IRIX 6.5.
+
+@item
+This function fails to parse @samp{NaN()} on some platforms:
+glibc-2.3.2, IRIX 6.5, mingw, Haiku.
+
+@item
+This function fails to parse @samp{NaN(@var{n-char-sequence})} on some
+platforms:
+IRIX 6.5.
+
+@item
+This function parses @samp{NaN(@var{n-char-sequence})}, but returns
+the wrong end pointer on some platforms:
+glibc-2.3.2, mingw, Haiku.
+
+@item
+This function fails to parse C99 hexadecimal floating point on some
+platforms:
+IRIX 6.5, mingw.
@end itemize
Portability problems not fixed by Gnulib:
@itemize
@item
-This function is missing on some platforms:
-NetBSD 3.0, OpenBSD 3.8, Minix 3.1.8, IRIX 6.5, OSF/1 4.0, Solaris 9, Cygwin, MSVC 9, Interix 3.5, BeOS, Android 4.4.
+The replacement function does not always return correctly rounded results.
@end itemize
# endif
#endif
+#if @GNULIB_STRTOLD@
+ /* Parse a 'long double' from STRING, updating ENDP if appropriate. */
+# if @REPLACE_STRTOLD@
+# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+# define strtold rpl_strtold
+# endif
+_GL_FUNCDECL_RPL (strtold, long double, (const char *str, char **endp)
+ _GL_ARG_NONNULL ((1)));
+_GL_CXXALIAS_RPL (strtold, long double, (const char *str, char **endp));
+# else
+# if !@HAVE_STRTOLD@
+_GL_FUNCDECL_SYS (strtold, long double, (const char *str, char **endp)
+ _GL_ARG_NONNULL ((1)));
+# endif
+_GL_CXXALIAS_SYS (strtold, long double, (const char *str, char **endp));
+# endif
+_GL_CXXALIASWARN (strtold);
+#elif defined GNULIB_POSIXCHECK
+# undef strtold
+# if HAVE_RAW_DECL_STRTOLD
+_GL_WARN_ON_USE (strtold, "strtold is unportable - "
+ "use gnulib module strtold for portability");
+# endif
+#endif
+
#if @GNULIB_STRTOLL@
/* Parse a signed integer whose textual representation starts at STRING.
The integer is expected to be in base BASE (2 <= BASE <= 36); if BASE == 0,
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. */
-#include <config.h>
+#if ! defined USE_LONG_DOUBLE
+# include <config.h>
+#endif
+/* Specification. */
#include <stdlib.h>
#include <ctype.h>
#include "c-ctype.h"
-#ifndef HAVE_LDEXP_IN_LIBC
-#define HAVE_LDEXP_IN_LIBC 0
+#undef MIN
+#undef MAX
+#ifdef USE_LONG_DOUBLE
+# define STRTOD strtold
+# define LDEXP ldexpl
+# if defined __hpux && defined __hppa
+ /* We cannot call strtold on HP-UX/hppa, because its return type is a struct,
+ not a 'long double'. */
+# define HAVE_UNDERLYING_STRTOD 0
+# else
+# define HAVE_UNDERLYING_STRTOD HAVE_STRTOLD
+# endif
+# define DOUBLE long double
+# define MIN LDBL_MIN
+# define MAX LDBL_MAX
+# define L_(literal) literal##L
+#else
+# define STRTOD strtod
+# define LDEXP ldexp
+# define HAVE_UNDERLYING_STRTOD 1
+# define DOUBLE double
+# define MIN DBL_MIN
+# define MAX DBL_MAX
+# define L_(literal) literal
+#endif
+
+#if (defined USE_LONG_DOUBLE ? HAVE_LDEXPM_IN_LIBC : HAVE_LDEXP_IN_LIBC)
+# define USE_LDEXP 1
+#else
+# define USE_LDEXP 0
#endif
/* Return true if C is a space in the current locale, avoiding
return isspace (uc) != 0;
}
-#if !HAVE_LDEXP_IN_LIBC
- #define ldexp dummy_ldexp
+#if !USE_LDEXP
+ #undef LDEXP
+ #define LDEXP dummy_ldexp
/* A dummy definition that will never be invoked. */
- static double ldexp (double x _GL_UNUSED, int exponent _GL_UNUSED)
+ static DOUBLE LDEXP (DOUBLE x _GL_UNUSED, int exponent _GL_UNUSED)
{
abort ();
- return 0.0;
+ return L_(0.0);
}
#endif
/* Return X * BASE**EXPONENT. Return an extreme value and set errno
to ERANGE if underflow or overflow occurs. */
-static double
-scale_radix_exp (double x, int radix, long int exponent)
+static DOUBLE
+scale_radix_exp (DOUBLE x, int radix, long int exponent)
{
/* If RADIX == 10, this code is neither precise nor fast; it is
merely a straightforward and relatively portable approximation.
long int e = exponent;
- if (HAVE_LDEXP_IN_LIBC && radix == 2)
- return ldexp (x, e < INT_MIN ? INT_MIN : INT_MAX < e ? INT_MAX : e);
+ if (USE_LDEXP && radix == 2)
+ return LDEXP (x, e < INT_MIN ? INT_MIN : INT_MAX < e ? INT_MAX : e);
else
{
- double r = x;
+ DOUBLE r = x;
if (r != 0)
{
{
while (e-- != 0)
{
- if (r < -DBL_MAX / radix)
+ if (r < -MAX / radix)
{
errno = ERANGE;
return -HUGE_VAL;
}
- else if (DBL_MAX / radix < r)
+ else if (MAX / radix < r)
{
errno = ERANGE;
return HUGE_VAL;
radix RADIX (either 10 or 2) exponent, and exponent character
EXPCHAR. To convert from a number of digits to a radix exponent,
multiply by RADIX_MULTIPLIER (either 1 or 4). */
-static double
+static DOUBLE
parse_number (const char *nptr,
int base, int radix, int radix_multiplier, char expchar,
char **endptr)
const char *s = nptr;
bool got_dot = false;
long int exponent = 0;
- double num = 0;
+ DOUBLE num = 0;
for (;; ++s)
{
break;
/* Make sure that multiplication by base will not overflow. */
- if (num <= DBL_MAX / base)
+ if (num <= MAX / base)
num = num * base + digit;
else
{
/* The value of the digit doesn't matter, since we have already
- gotten as many digits as can be represented in a 'double'.
+ gotten as many digits as can be represented in a 'DOUBLE'.
This doesn't necessarily mean the result will overflow.
The exponent may reduce it to within range.
return scale_radix_exp (num, radix, exponent);
}
-static double underlying_strtod (const char *, char **);
-
/* HP cc on HP-UX 10.20 has a bug with the constant expression -0.0.
ICC 10.0 has a bug when optimizing the expression -zero.
- The expression -DBL_MIN * DBL_MIN does not work when cross-compiling
+ The expression -MIN * MIN does not work when cross-compiling
to PowerPC on Mac OS X 10.5. */
#if defined __hpux || defined __sgi || defined __ICC
-static double
+static DOUBLE
compute_minus_zero (void)
{
- return -DBL_MIN * DBL_MIN;
+ return -MIN * MIN;
}
# define minus_zero compute_minus_zero ()
#else
-double minus_zero = -0.0;
+DOUBLE minus_zero = -0.0;
#endif
-/* Convert NPTR to a double. If ENDPTR is not NULL, a pointer to the
+/* Convert NPTR to a DOUBLE. If ENDPTR is not NULL, a pointer to the
character after the last one used in the number is put in *ENDPTR. */
-double
-strtod (const char *nptr, char **endptr)
+DOUBLE
+STRTOD (const char *nptr, char **endptr)
+#if HAVE_UNDERLYING_STRTOD
+# ifdef USE_LONG_DOUBLE
+# undef strtold
+# else
+# undef strtod
+# endif
+#else
+# undef STRTOD
+# define STRTOD(NPTR,ENDPTR) parse_number (NPTR, 10, 10, 1, 'e', ENDPTR)
+#endif
+/* From here on, STRTOD refers to the underlying implementation. It needs
+ to handle only finite unsigned decimal numbers with non-null ENDPTR. */
{
bool negative = false;
/* The number so far. */
- double num;
+ DOUBLE num;
const char *s = nptr;
const char *end;
if (*s == '-' || *s == '+')
++s;
- num = underlying_strtod (s, &endbuf);
+ num = STRTOD (s, &endbuf);
end = endbuf;
if (c_isdigit (s[*s == '.']))
else
{
/* If "1e 1" was misparsed as 10.0 instead of 1.0, re-do the
- underlying strtod on a copy of the original string
+ underlying STRTOD on a copy of the original string
truncated to avoid the bug. */
const char *e = s + 1;
while (e < end && c_tolower (*e) != 'e')
else
{
dup[e - s] = '\0';
- num = underlying_strtod (dup, &endbuf);
+ num = STRTOD (dup, &endbuf);
saved_errno = errno;
free (dup);
errno = saved_errno;
return minus_zero;
return negative ? -num : num;
}
-
-/* The underlying strtod implementation. This must be defined
- after strtod because it #undefs strtod. */
-static double
-underlying_strtod (const char *nptr, char **endptr)
-{
-#undef strtod
- return strtod (nptr, endptr);
-}
--- /dev/null
+/* Convert string to 'long double'.
+ Copyright (C) 2019 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 <https://www.gnu.org/licenses/>. */
+
+/* Written by Bruno Haible <bruno@clisp.org>, 2019. */
+
+#include <config.h>
+
+#if HAVE_SAME_LONG_DOUBLE_AS_DOUBLE
+
+/* Specification. */
+# include <stdlib.h>
+
+long double
+strtold (const char *str, char **endp)
+{
+ return strtod (str, endp);
+}
+
+#else
+
+# define USE_LONG_DOUBLE
+# include "strtod.c"
+
+#endif
-# stdlib_h.m4 serial 47
+# stdlib_h.m4 serial 48
dnl Copyright (C) 2007-2019 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
initstate initstate_r mbtowc mkdtemp mkostemp mkostemps mkstemp mkstemps
posix_openpt ptsname ptsname_r qsort_r random random_r reallocarray
realpath rpmatch secure_getenv setenv setstate setstate_r srandom
- srandom_r strtod strtoll strtoull unlockpt unsetenv])
+ srandom_r strtod strtold strtoll strtoull unlockpt unsetenv])
])
AC_DEFUN([gl_STDLIB_MODULE_INDICATOR],
GNULIB_SECURE_GETENV=0; AC_SUBST([GNULIB_SECURE_GETENV])
GNULIB_SETENV=0; AC_SUBST([GNULIB_SETENV])
GNULIB_STRTOD=0; AC_SUBST([GNULIB_STRTOD])
+ GNULIB_STRTOLD=0; AC_SUBST([GNULIB_STRTOLD])
GNULIB_STRTOLL=0; AC_SUBST([GNULIB_STRTOLL])
GNULIB_STRTOULL=0; AC_SUBST([GNULIB_STRTOULL])
GNULIB_SYSTEM_POSIX=0; AC_SUBST([GNULIB_SYSTEM_POSIX])
HAVE_SETSTATE=1; AC_SUBST([HAVE_SETSTATE])
HAVE_DECL_SETSTATE=1; AC_SUBST([HAVE_DECL_SETSTATE])
HAVE_STRTOD=1; AC_SUBST([HAVE_STRTOD])
+ HAVE_STRTOLD=1; AC_SUBST([HAVE_STRTOLD])
HAVE_STRTOLL=1; AC_SUBST([HAVE_STRTOLL])
HAVE_STRTOULL=1; AC_SUBST([HAVE_STRTOULL])
HAVE_STRUCT_RANDOM_DATA=1; AC_SUBST([HAVE_STRUCT_RANDOM_DATA])
REPLACE_SETENV=0; AC_SUBST([REPLACE_SETENV])
REPLACE_SETSTATE=0; AC_SUBST([REPLACE_SETSTATE])
REPLACE_STRTOD=0; AC_SUBST([REPLACE_STRTOD])
+ REPLACE_STRTOLD=0; AC_SUBST([REPLACE_STRTOLD])
REPLACE_UNSETENV=0; AC_SUBST([REPLACE_UNSETENV])
REPLACE_WCTOMB=0; AC_SUBST([REPLACE_WCTOMB])
])
--- /dev/null
+# strtold.m4 serial 1
+dnl Copyright (C) 2002-2003, 2006-2019 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_FUNC_STRTOLD],
+[
+ AC_REQUIRE([gl_STDLIB_H_DEFAULTS])
+ AC_REQUIRE([gl_LONG_DOUBLE_VS_DOUBLE])
+ AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles
+ AC_CHECK_FUNCS_ONCE([strtold])
+ if test $ac_cv_func_strtold != yes; then
+ HAVE_STRTOLD=0
+ else
+ AC_CACHE_CHECK([whether strtold obeys C99], [gl_cv_func_strtold_works],
+ [AC_RUN_IFELSE([AC_LANG_PROGRAM([[
+#include <stdlib.h>
+#include <math.h>
+#include <errno.h>
+/* Compare two numbers with ==.
+ This is a separate function because IRIX 6.5 "cc -O" miscompiles an
+ 'x == x' test. */
+static int
+numeric_equal (long double x, long double y)
+{
+ return x == y;
+}
+]], [[
+ int result = 0;
+ {
+ /* Under Solaris 2.4, strtod returns the wrong value for the
+ terminating character under some conditions. */
+ const char *string = "NaN";
+ char *term;
+ strtod (string, &term);
+ if (term != string && *(term - 1) == 0)
+ result |= 2;
+ }
+ {
+ /* Older glibc and Cygwin mis-parse "-0x". */
+ const char *string = "-0x";
+ char *term;
+ long double value = strtold (string, &term);
+ long double zero = 0.0L;
+ if (1.0L / value != -1.0L / zero || term != (string + 2))
+ result |= 4;
+ }
+ {
+ /* IRIX 6.5, mingw do not parse hex floats. */
+ const char *string = "0XaP+1";
+ char *term;
+ long double value = strtold (string, &term);
+ if (value != 20.0L || term != (string + 6))
+ result |= 8;
+ }
+ {
+ /* IRIX 6.5 does not parse infinities. HP-UX 11.31/ia64 parses inf,
+ but mistakenly sets errno. */
+ const char *string = "inf";
+ char *term;
+ long double value;
+ errno = 0;
+ value = strtod (string, &term);
+ if (value != HUGE_VAL || term != (string + 3) || errno)
+ result |= 16;
+ }
+ {
+ /* glibc-2.3.2, IRIX 6.5, mingw, Haiku misparse "nan()". */
+ const char *string = "nan()";
+ char *term;
+ long double value = strtold (string, &term);
+ if (numeric_equal (value, value) || term != (string + 5))
+ result |= 32;
+ }
+ {
+ /* Mac OS X 10.5, IRIX 6.5 misparse "nan(". */
+ const char *string = "nan(";
+ char *term;
+ long double value = strtold (string, &term);
+ if (numeric_equal (value, value) || term != (string + 3))
+ result |= 64;
+ }
+ return result;
+]])],
+ [gl_cv_func_strtold_works=yes],
+ [gl_cv_func_strtold_works=no],
+ [dnl The last known bugs in glibc strtold(), as of this writing,
+ dnl were fixed in version 2.8
+ AC_EGREP_CPP([Lucky user],
+ [
+#include <features.h>
+#ifdef __GNU_LIBRARY__
+ #if ((__GLIBC__ == 2 && __GLIBC_MINOR__ >= 8) || (__GLIBC__ > 2)) \
+ && !defined __UCLIBC__
+ Lucky user
+ #endif
+#endif
+ ],
+ [gl_cv_func_strtold_works="guessing yes"],
+ [gl_cv_func_strtod_works="guessing no"])
+ ])
+ ])
+ case "$gl_cv_func_strtold_works" in
+ *yes) ;;
+ *)
+ REPLACE_STRTOLD=1
+ ;;
+ esac
+ fi
+])
+
+# Prerequisites of lib/strtold.c.
+AC_DEFUN([gl_PREREQ_STRTOLD], [
+ AC_REQUIRE([gl_CHECK_LDEXPL_NO_LIBM])
+ if test $gl_cv_func_ldexpl_no_libm = yes; then
+ AC_DEFINE([HAVE_LDEXPL_IN_LIBC], [1],
+ [Define if the ldexpl function is available in libc.])
+ fi
+])
-e 's/@''GNULIB_SECURE_GETENV''@/$(GNULIB_SECURE_GETENV)/g' \
-e 's/@''GNULIB_SETENV''@/$(GNULIB_SETENV)/g' \
-e 's/@''GNULIB_STRTOD''@/$(GNULIB_STRTOD)/g' \
+ -e 's/@''GNULIB_STRTOLD''@/$(GNULIB_STRTOLD)/g' \
-e 's/@''GNULIB_STRTOLL''@/$(GNULIB_STRTOLL)/g' \
-e 's/@''GNULIB_STRTOULL''@/$(GNULIB_STRTOULL)/g' \
-e 's/@''GNULIB_SYSTEM_POSIX''@/$(GNULIB_SYSTEM_POSIX)/g' \
-e 's|@''HAVE_SETSTATE''@|$(HAVE_SETSTATE)|g' \
-e 's|@''HAVE_DECL_SETSTATE''@|$(HAVE_DECL_SETSTATE)|g' \
-e 's|@''HAVE_STRTOD''@|$(HAVE_STRTOD)|g' \
+ -e 's|@''HAVE_STRTOLD''@|$(HAVE_STRTOLD)|g' \
-e 's|@''HAVE_STRTOLL''@|$(HAVE_STRTOLL)|g' \
-e 's|@''HAVE_STRTOULL''@|$(HAVE_STRTOULL)|g' \
-e 's|@''HAVE_STRUCT_RANDOM_DATA''@|$(HAVE_STRUCT_RANDOM_DATA)|g' \
-e 's|@''REPLACE_SETENV''@|$(REPLACE_SETENV)|g' \
-e 's|@''REPLACE_SETSTATE''@|$(REPLACE_SETSTATE)|g' \
-e 's|@''REPLACE_STRTOD''@|$(REPLACE_STRTOD)|g' \
+ -e 's|@''REPLACE_STRTOLD''@|$(REPLACE_STRTOLD)|g' \
-e 's|@''REPLACE_UNSETENV''@|$(REPLACE_UNSETENV)|g' \
-e 's|@''REPLACE_WCTOMB''@|$(REPLACE_WCTOMB)|g' \
-e '/definitions of _GL_FUNCDECL_RPL/r $(CXXDEFS_H)' \
--- /dev/null
+Description:
+strtold() function: convert string to 'long double'.
+
+Files:
+lib/strtold.c
+lib/strtod.c
+m4/strtold.m4
+m4/math_h.m4
+m4/ldexpl.m4
+
+Depends-on:
+stdlib
+c-ctype [test $HAVE_STRTOLD = 0 || test $REPLACE_STRTOLD = 1]
+math [test $HAVE_STRTOLD = 0 || test $REPLACE_STRTOLD = 1]
+stdbool [test $HAVE_STRTOLD = 0 || test $REPLACE_STRTOLD = 1]
+strtod [{ test $HAVE_STRTOLD = 0 || test $REPLACE_STRTOLD = 1; } && test $HAVE_SAME_LONG_DOUBLE_AS_DOUBLE = 1]
+
+configure.ac:
+gl_FUNC_STRTOLD
+if test $HAVE_STRTOLD = 0 || test $REPLACE_STRTOLD = 1; then
+ AC_LIBOBJ([strtold])
+ gl_PREREQ_STRTOLD
+fi
+gl_STDLIB_MODULE_INDICATOR([strtold])
+
+Makefile.am:
+
+Include:
+<stdlib.h>
+
+License:
+LGPL
+
+Maintainer:
+all