bug-gnulib@gnu.org mirror (unofficial)
 help / color / mirror / Atom feed
* new module 'strtold'
@ 2019-01-30  3:05 Bruno Haible
  2019-01-30  5:42 ` Pádraig Brady
  2019-02-01  2:16 ` strtod, strtold: various fixes Bruno Haible
  0 siblings, 2 replies; 6+ messages in thread
From: Bruno Haible @ 2019-01-30  3:05 UTC (permalink / raw)
  To: bug-gnulib

[-- Attachment #1: Type: text/plain, Size: 2514 bytes --]

Hi,

coreutils has recently started to use strtold() [1]. This causes compilation
errors on Android 4.3:

  CC       lib/cl-strtod.o
  CC       lib/cl-strtold.o
In file included from ../lib/cl-strtold.c:2:
../lib/cl-strtod.c: In function 'cl_strtold':
../lib/cl-strtod.c:62: warning: implicit declaration of function 'strtold'

  CCLD     src/printf
lib/libcoreutils.a(cl-strtold.o): In function `cl_strtold':
cl-strtold.c:(.text+0x14): undefined reference to `strtold'
collect2: ld returned 1 exit status
make[2]: *** [src/printf] Error 1

  CCLD     src/seq
lib/libcoreutils.a(cl-strtold.o): In function `cl_strtold':
cl-strtold.c:(.text+0x14): undefined reference to `strtold'
collect2: ld returned 1 exit status
make[2]: *** [src/seq] Error 1

and similarly on Solaris 9 (both lack the strtold() function)
and on HP-UX 11/hppa (where the function is declared to return a struct,
not a 'long double').

And of course the function is not POSIX compliant on some platforms.

Here comes a new gnulib module 'strtold'. Tested on

- a glibc 2.3.2 system (Fedora 1)
- Mac OS X 10.5, Mac OS X 10.4/PowerPC
- FreeBSD 11.0
- NetBSD 7.1
- OpenBSD 6.0
- Minix 3.3
- AIX 7.1
- HP-UX 11.31/hppa
- HP-UX 11.31/ia64 (test failure in line 574, also in test-strtod.c:574)
- IRIX 6.5
- Solaris 9/SPARC, Solaris 10/SPARC, Solaris 10/x86
- Cygwin
- mingw
- Haiku
- Android 4.3

Bruno

[1] https://git.savannah.gnu.org/gitweb/?p=coreutils.git;a=commitdiff;h=eb73e23f31f4ce363b1505cc77424832d5c39063


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>

	strtold: Add tests.
	* tests/test-strtold.c: New file, based on tests/test-strtod.c.
	* modules/strtold-tests: New file.


[-- Attachment #2: 0001-strtold-New-module.patch --]
[-- Type: text/x-patch, Size: 23187 bytes --]

From ebad0cfebbb201a4e645495d244f899a39a305e3 Mon Sep 17 00:00:00 2001
From: Bruno Haible <bruno@clisp.org>
Date: Wed, 30 Jan 2019 03:52:31 +0100
Subject: [PATCH 1/2] 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.
---
 ChangeLog                        |  22 +++++++
 doc/posix-functions/strtold.texi |  54 +++++++++++++++++-
 lib/stdlib.in.h                  |  25 ++++++++
 lib/strtod.c                     | 113 +++++++++++++++++++++++-------------
 lib/strtold.c                    |  37 ++++++++++++
 m4/stdlib_h.m4                   |   7 ++-
 m4/strtold.m4                    | 120 +++++++++++++++++++++++++++++++++++++++
 modules/stdlib                   |   3 +
 modules/strtold                  |  35 ++++++++++++
 9 files changed, 371 insertions(+), 45 deletions(-)
 create mode 100644 lib/strtold.c
 create mode 100644 m4/strtold.m4
 create mode 100644 modules/strtold

diff --git a/ChangeLog b/ChangeLog
index 51daf91..ef10452 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,27 @@
 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.
 	* modules/strtod (Depends-on): Add 'math'.
 
diff --git a/doc/posix-functions/strtold.texi b/doc/posix-functions/strtold.texi
index ff6f195..f136c54 100644
--- a/doc/posix-functions/strtold.texi
+++ b/doc/posix-functions/strtold.texi
@@ -4,15 +4,63 @@
 
 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
diff --git a/lib/stdlib.in.h b/lib/stdlib.in.h
index be8ab3b..b70444d 100644
--- a/lib/stdlib.in.h
+++ b/lib/stdlib.in.h
@@ -948,6 +948,31 @@ _GL_WARN_ON_USE (strtod, "strtod is unportable - "
 # 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,
diff --git a/lib/strtod.c b/lib/strtod.c
index 9a28f4b..1d1ba27 100644
--- a/lib/strtod.c
+++ b/lib/strtod.c
@@ -14,8 +14,11 @@
    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>
@@ -28,8 +31,36 @@
 
 #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
@@ -41,20 +72,21 @@ locale_isspace (char c)
   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.
@@ -63,11 +95,11 @@ scale_radix_exp (double x, int radix, long int exponent)
 
   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)
         {
@@ -87,12 +119,12 @@ scale_radix_exp (double x, int radix, long int exponent)
             {
               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;
@@ -113,7 +145,7 @@ scale_radix_exp (double x, int radix, long int exponent)
    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)
@@ -121,7 +153,7 @@ parse_number (const char *nptr,
   const char *s = nptr;
   bool got_dot = false;
   long int exponent = 0;
-  double num = 0;
+  DOUBLE num = 0;
 
   for (;; ++s)
     {
@@ -141,12 +173,12 @@ parse_number (const char *nptr,
         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.
 
@@ -185,32 +217,42 @@ parse_number (const char *nptr,
   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;
@@ -226,7 +268,7 @@ strtod (const char *nptr, char **endptr)
   if (*s == '-' || *s == '+')
     ++s;
 
-  num = underlying_strtod (s, &endbuf);
+  num = STRTOD (s, &endbuf);
   end = endbuf;
 
   if (c_isdigit (s[*s == '.']))
@@ -261,7 +303,7 @@ strtod (const char *nptr, char **endptr)
       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')
@@ -279,7 +321,7 @@ strtod (const char *nptr, char **endptr)
               else
                 {
                   dup[e - s] = '\0';
-                  num = underlying_strtod (dup, &endbuf);
+                  num = STRTOD (dup, &endbuf);
                   saved_errno = errno;
                   free (dup);
                   errno = saved_errno;
@@ -344,12 +386,3 @@ strtod (const char *nptr, char **endptr)
     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);
-}
diff --git a/lib/strtold.c b/lib/strtold.c
new file mode 100644
index 0000000..3791303
--- /dev/null
+++ b/lib/strtold.c
@@ -0,0 +1,37 @@
+/* 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
diff --git a/m4/stdlib_h.m4 b/m4/stdlib_h.m4
index c5db804..6121602 100644
--- a/m4/stdlib_h.m4
+++ b/m4/stdlib_h.m4
@@ -1,4 +1,4 @@
-# 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,
@@ -26,7 +26,7 @@ AC_DEFUN([gl_STDLIB_H],
     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],
@@ -68,6 +68,7 @@ AC_DEFUN([gl_STDLIB_H_DEFAULTS],
   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])
@@ -105,6 +106,7 @@ AC_DEFUN([gl_STDLIB_H_DEFAULTS],
   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])
@@ -128,6 +130,7 @@ AC_DEFUN([gl_STDLIB_H_DEFAULTS],
   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])
 ])
diff --git a/m4/strtold.m4 b/m4/strtold.m4
new file mode 100644
index 0000000..117b7f1
--- /dev/null
+++ b/m4/strtold.m4
@@ -0,0 +1,120 @@
+# 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
+])
diff --git a/modules/stdlib b/modules/stdlib
index a43f79c..ea838c9 100644
--- a/modules/stdlib
+++ b/modules/stdlib
@@ -59,6 +59,7 @@ stdlib.h: stdlib.in.h $(top_builddir)/config.status $(CXXDEFS_H) \
 	      -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' \
@@ -95,6 +96,7 @@ stdlib.h: stdlib.in.h $(top_builddir)/config.status $(CXXDEFS_H) \
 	      -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' \
@@ -118,6 +120,7 @@ stdlib.h: stdlib.in.h $(top_builddir)/config.status $(CXXDEFS_H) \
 	      -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)' \
diff --git a/modules/strtold b/modules/strtold
new file mode 100644
index 0000000..f99b9e6
--- /dev/null
+++ b/modules/strtold
@@ -0,0 +1,35 @@
+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
-- 
2.7.4


[-- Attachment #3: 0002-strtold-Add-tests.patch --]
[-- Type: text/x-patch, Size: 29385 bytes --]

From c39d926ef784d4c9de90e48a5aca16d0acd57512 Mon Sep 17 00:00:00 2001
From: Bruno Haible <bruno@clisp.org>
Date: Wed, 30 Jan 2019 03:53:43 +0100
Subject: [PATCH 2/2] strtold: Add tests.

* tests/test-strtold.c: New file, based on tests/test-strtod.c.
* modules/strtold-tests: New file.
---
 ChangeLog             |   6 +
 modules/strtold-tests |  16 +
 tests/test-strtold.c  | 986 ++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 1008 insertions(+)
 create mode 100644 modules/strtold-tests
 create mode 100644 tests/test-strtold.c

diff --git a/ChangeLog b/ChangeLog
index ef10452..5ab5d83 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,11 @@
 2019-01-29  Bruno Haible  <bruno@clisp.org>
 
+	strtold: Add tests.
+	* tests/test-strtold.c: New file, based on tests/test-strtod.c.
+	* modules/strtold-tests: New file.
+
+2019-01-29  Bruno Haible  <bruno@clisp.org>
+
 	strtold: New module.
 	* lib/stdlib.in.h (strtold): New declaration.
 	* lib/strtold.c: New file.
diff --git a/modules/strtold-tests b/modules/strtold-tests
new file mode 100644
index 0000000..8a812d2
--- /dev/null
+++ b/modules/strtold-tests
@@ -0,0 +1,16 @@
+Files:
+tests/test-strtold.c
+tests/signature.h
+tests/minus-zero.h
+tests/macros.h
+
+Depends-on:
+float
+isnanl-nolibm
+signbit
+
+configure.ac:
+
+Makefile.am:
+TESTS += test-strtold
+check_PROGRAMS += test-strtold
diff --git a/tests/test-strtold.c b/tests/test-strtold.c
new file mode 100644
index 0000000..cd31787
--- /dev/null
+++ b/tests/test-strtold.c
@@ -0,0 +1,986 @@
+/*
+ * Copyright (C) 2008-2019 Free Software Foundation, Inc.
+ * Written by Eric Blake
+ *
+ * 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/>.  */
+
+#include <config.h>
+
+#include <stdlib.h>
+
+#include "signature.h"
+SIGNATURE_CHECK (strtold, long double, (char const *, char **));
+
+#include <errno.h>
+#include <float.h>
+#include <math.h>
+#include <string.h>
+
+#include "isnanl-nolibm.h"
+#include "minus-zero.h"
+#include "macros.h"
+
+/* Avoid requiring -lm just for fabsl.  */
+#define FABSL(d) ((d) < 0.0L ? -(d) : (d))
+
+int
+main (void)
+{
+  int status = 0;
+  /* Subject sequence empty or invalid.  */
+  {
+    const char input[] = "";
+    char *ptr;
+    long double result;
+    errno = 0;
+    result = strtold (input, &ptr);
+    ASSERT (result == 0.0L);
+    ASSERT (!signbit (result));
+    ASSERT (ptr == input);
+    ASSERT (errno == 0 || errno == EINVAL);
+  }
+  {
+    const char input[] = " ";
+    char *ptr;
+    long double result;
+    errno = 0;
+    result = strtold (input, &ptr);
+    ASSERT (result == 0.0L);
+    ASSERT (!signbit (result));
+    ASSERT (ptr == input);
+    ASSERT (errno == 0 || errno == EINVAL);
+  }
+  {
+    const char input[] = " +";
+    char *ptr;
+    long double result;
+    errno = 0;
+    result = strtold (input, &ptr);
+    ASSERT (result == 0.0L);
+    ASSERT (!signbit (result));
+    ASSERT (ptr == input);
+    ASSERT (errno == 0 || errno == EINVAL);
+  }
+  {
+    const char input[] = " .";
+    char *ptr;
+    long double result;
+    errno = 0;
+    result = strtold (input, &ptr);
+    ASSERT (result == 0.0L);
+    ASSERT (!signbit (result));
+    ASSERT (ptr == input);
+    ASSERT (errno == 0 || errno == EINVAL);
+  }
+  {
+    const char input[] = " .e0";
+    char *ptr;
+    long double result;
+    errno = 0;
+    result = strtold (input, &ptr);
+    ASSERT (result == 0.0L);
+    ASSERT (!signbit (result));
+    ASSERT (ptr == input);              /* IRIX 6.5 */
+    ASSERT (errno == 0 || errno == EINVAL);
+  }
+  {
+    const char input[] = " +.e-0";
+    char *ptr;
+    long double result;
+    errno = 0;
+    result = strtold (input, &ptr);
+    ASSERT (result == 0.0L);
+    ASSERT (!signbit (result));
+    ASSERT (ptr == input);              /* IRIX 6.5 */
+    ASSERT (errno == 0 || errno == EINVAL);
+  }
+  {
+    const char input[] = " in";
+    char *ptr;
+    long double result;
+    errno = 0;
+    result = strtold (input, &ptr);
+    ASSERT (result == 0.0L);
+    ASSERT (!signbit (result));
+    ASSERT (ptr == input);
+    ASSERT (errno == 0 || errno == EINVAL);
+  }
+  {
+    const char input[] = " na";
+    char *ptr;
+    long double result;
+    errno = 0;
+    result = strtold (input, &ptr);
+    ASSERT (result == 0.0L);
+    ASSERT (!signbit (result));
+    ASSERT (ptr == input);
+    ASSERT (errno == 0 || errno == EINVAL);
+  }
+
+  /* Simple floating point values.  */
+  {
+    const char input[] = "1";
+    char *ptr;
+    long double result;
+    errno = 0;
+    result = strtold (input, &ptr);
+    ASSERT (result == 1.0L);
+    ASSERT (ptr == input + 1);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "1.";
+    char *ptr;
+    long double result;
+    errno = 0;
+    result = strtold (input, &ptr);
+    ASSERT (result == 1.0L);
+    ASSERT (ptr == input + 2);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = ".5";
+    char *ptr;
+    long double result;
+    errno = 0;
+    result = strtold (input, &ptr);
+    /* FIXME - gnulib's version is rather inaccurate.  It would be
+       nice to guarantee an exact result, but for now, we settle for a
+       1-ulp error.  */
+    ASSERT (FABSL (result - 0.5L) < LDBL_EPSILON);
+    ASSERT (ptr == input + 2);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = " 1";
+    char *ptr;
+    long double result;
+    errno = 0;
+    result = strtold (input, &ptr);
+    ASSERT (result == 1.0L);
+    ASSERT (ptr == input + 2);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "+1";
+    char *ptr;
+    long double result;
+    errno = 0;
+    result = strtold (input, &ptr);
+    ASSERT (result == 1.0L);
+    ASSERT (ptr == input + 2);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "-1";
+    char *ptr;
+    long double result;
+    errno = 0;
+    result = strtold (input, &ptr);
+    ASSERT (result == -1.0L);
+    ASSERT (ptr == input + 2);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "1e0";
+    char *ptr;
+    long double result;
+    errno = 0;
+    result = strtold (input, &ptr);
+    ASSERT (result == 1.0L);
+    ASSERT (ptr == input + 3);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "1e+0";
+    char *ptr;
+    long double result;
+    errno = 0;
+    result = strtold (input, &ptr);
+    ASSERT (result == 1.0L);
+    ASSERT (ptr == input + 4);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "1e-0";
+    char *ptr;
+    long double result;
+    errno = 0;
+    result = strtold (input, &ptr);
+    ASSERT (result == 1.0L);
+    ASSERT (ptr == input + 4);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "1e1";
+    char *ptr;
+    long double result;
+    errno = 0;
+    result = strtold (input, &ptr);
+    ASSERT (result == 10.0L);
+    ASSERT (ptr == input + 3);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "5e-1";
+    char *ptr;
+    long double result;
+    errno = 0;
+    result = strtold (input, &ptr);
+    /* FIXME - gnulib's version is rather inaccurate.  It would be
+       nice to guarantee an exact result, but for now, we settle for a
+       1-ulp error.  */
+    ASSERT (FABSL (result - 0.5L) < LDBL_EPSILON);
+    ASSERT (ptr == input + 4);
+    ASSERT (errno == 0);
+  }
+
+  /* Zero.  */
+  {
+    const char input[] = "0";
+    char *ptr;
+    long double result;
+    errno = 0;
+    result = strtold (input, &ptr);
+    ASSERT (result == 0.0L);
+    ASSERT (!signbit (result));
+    ASSERT (ptr == input + 1);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = ".0";
+    char *ptr;
+    long double result;
+    errno = 0;
+    result = strtold (input, &ptr);
+    ASSERT (result == 0.0L);
+    ASSERT (!signbit (result));
+    ASSERT (ptr == input + 2);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "0e0";
+    char *ptr;
+    long double result;
+    errno = 0;
+    result = strtold (input, &ptr);
+    ASSERT (result == 0.0L);
+    ASSERT (!signbit (result));
+    ASSERT (ptr == input + 3);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "0e+9999999";
+    char *ptr;
+    long double result;
+    errno = 0;
+    result = strtold (input, &ptr);
+    ASSERT (result == 0.0L);
+    ASSERT (!signbit (result));
+    ASSERT (ptr == input + 10);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "0e-9999999";
+    char *ptr;
+    long double result;
+    errno = 0;
+    result = strtold (input, &ptr);
+    ASSERT (result == 0.0L);
+    ASSERT (!signbit (result));
+    ASSERT (ptr == input + 10);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "-0";
+    char *ptr;
+    long double result;
+    errno = 0;
+    result = strtold (input, &ptr);
+    ASSERT (result == 0.0L);
+    ASSERT (!!signbit (result) == !!signbit (minus_zerol)); /* IRIX 6.5 */
+    ASSERT (ptr == input + 2);
+    ASSERT (errno == 0);
+  }
+
+  /* Suffixes.  */
+  {
+    const char input[] = "1f";
+    char *ptr;
+    long double result;
+    errno = 0;
+    result = strtold (input, &ptr);
+    ASSERT (result == 1.0L);
+    ASSERT (ptr == input + 1);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "1.f";
+    char *ptr;
+    long double result;
+    errno = 0;
+    result = strtold (input, &ptr);
+    ASSERT (result == 1.0L);
+    ASSERT (ptr == input + 2);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "1e";
+    char *ptr;
+    long double result;
+    errno = 0;
+    result = strtold (input, &ptr);
+    ASSERT (result == 1.0L);
+    ASSERT (ptr == input + 1);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "1e+";
+    char *ptr;
+    long double result;
+    errno = 0;
+    result = strtold (input, &ptr);
+    ASSERT (result == 1.0L);
+    ASSERT (ptr == input + 1);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "1e-";
+    char *ptr;
+    long double result;
+    errno = 0;
+    result = strtold (input, &ptr);
+    ASSERT (result == 1.0L);
+    ASSERT (ptr == input + 1);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "1E 2";
+    char *ptr;
+    long double result;
+    errno = 0;
+    result = strtold (input, &ptr);
+    ASSERT (result == 1.0L);            /* HP-UX 11.31/ia64, IRIX 6.5 */
+    ASSERT (ptr == input + 1);          /* HP-UX 11.31/ia64, IRIX 6.5 */
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "0x";
+    char *ptr;
+    long double result;
+    errno = 0;
+    result = strtold (input, &ptr);
+    ASSERT (result == 0.0L);
+    ASSERT (!signbit (result));
+    ASSERT (ptr == input + 1);          /* glibc-2.3.2, Mac OS X 10.5, Haiku */
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "00x1";
+    char *ptr;
+    long double result;
+    errno = 0;
+    result = strtold (input, &ptr);
+    ASSERT (result == 0.0L);
+    ASSERT (!signbit (result));
+    ASSERT (ptr == input + 2);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "-0x";
+    char *ptr;
+    long double result;
+    errno = 0;
+    result = strtold (input, &ptr);
+    ASSERT (result == 0.0L);
+    ASSERT (!!signbit (result) == !!signbit (minus_zerol)); /* Mac OS X 10.5, IRIX 6.5 */
+    ASSERT (ptr == input + 2);          /* glibc-2.3.2, Mac OS X 10.5, Haiku */
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "0xg";
+    char *ptr;
+    long double result;
+    errno = 0;
+    result = strtold (input, &ptr);
+    ASSERT (result == 0.0L);
+    ASSERT (!signbit (result));
+    ASSERT (ptr == input + 1);          /* glibc-2.3.2, Mac OS X 10.5, Haiku */
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "0xp";
+    char *ptr;
+    long double result;
+    errno = 0;
+    result = strtold (input, &ptr);
+    ASSERT (result == 0.0L);
+    ASSERT (!signbit (result));
+    ASSERT (ptr == input + 1);          /* glibc-2.3.2, Mac OS X 10.5, Haiku */
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "0XP";
+    char *ptr;
+    long double result;
+    errno = 0;
+    result = strtold (input, &ptr);
+    ASSERT (result == 0.0L);
+    ASSERT (!signbit (result));
+    ASSERT (ptr == input + 1);          /* glibc-2.3.2, Mac OS X 10.5, Haiku */
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "0x.";
+    char *ptr;
+    long double result;
+    errno = 0;
+    result = strtold (input, &ptr);
+    ASSERT (result == 0.0L);
+    ASSERT (!signbit (result));
+    ASSERT (ptr == input + 1);          /* glibc-2.3.2, Mac OS X 10.5, Haiku */
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "0xp+";
+    char *ptr;
+    long double result;
+    errno = 0;
+    result = strtold (input, &ptr);
+    ASSERT (result == 0.0L);
+    ASSERT (!signbit (result));
+    ASSERT (ptr == input + 1);          /* glibc-2.3.2, Mac OS X 10.5, Haiku */
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "0xp+1";
+    char *ptr;
+    long double result;
+    errno = 0;
+    result = strtold (input, &ptr);
+    ASSERT (result == 0.0L);
+    ASSERT (!signbit (result));
+    ASSERT (ptr == input + 1);          /* glibc-2.3.2, Mac OS X 10.5, Haiku */
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "0x.p+1";
+    char *ptr;
+    long double result;
+    errno = 0;
+    result = strtold (input, &ptr);
+    ASSERT (result == 0.0L);
+    ASSERT (!signbit (result));
+    ASSERT (ptr == input + 1);          /* glibc-2.3.2, Mac OS X 10.5, Haiku */
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "1p+1";
+    char *ptr;
+    long double result;
+    errno = 0;
+    result = strtold (input, &ptr);
+    ASSERT (result == 1.0L);
+    ASSERT (ptr == input + 1);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "1P+1";
+    char *ptr;
+    long double result;
+    errno = 0;
+    result = strtold (input, &ptr);
+    ASSERT (result == 1.0L);
+    ASSERT (ptr == input + 1);
+    ASSERT (errno == 0);
+  }
+
+  /* Overflow/underflow.  */
+  {
+    const char input[] = "1E1000000";
+    char *ptr;
+    long double result;
+    errno = 0;
+    result = strtold (input, &ptr);
+    ASSERT (result == HUGE_VALL);
+    ASSERT (ptr == input + 9);
+    ASSERT (errno == ERANGE);
+  }
+  {
+    const char input[] = "-1E1000000";
+    char *ptr;
+    long double result;
+    errno = 0;
+    result = strtold (input, &ptr);
+    ASSERT (result == -HUGE_VALL);
+    ASSERT (ptr == input + 10);
+    ASSERT (errno == ERANGE);
+  }
+  {
+    const char input[] = "1E-100000";
+    char *ptr;
+    long double result;
+    errno = 0;
+    result = strtold (input, &ptr);
+    ASSERT (0.0L <= result && result <= LDBL_MIN);
+    ASSERT (!signbit (result));
+    ASSERT (ptr == input + 9);
+    ASSERT (errno == ERANGE);
+  }
+  {
+    const char input[] = "-1E-100000";
+    char *ptr;
+    long double result;
+    errno = 0;
+    result = strtold (input, &ptr);
+    ASSERT (-LDBL_MIN <= result && result <= 0.0L);
+#if 0
+    /* FIXME - this is glibc bug 5995; POSIX allows returning positive
+       0 on negative underflow, even though quality of implementation
+       demands preserving the sign.  Disable this test until fixed
+       glibc is more prevalent.  */
+    ASSERT (!!signbit (result) == !!signbit (minus_zerol)); /* glibc-2.3.2, Haiku */
+#endif
+    ASSERT (ptr == input + 10);
+    ASSERT (errno == ERANGE);
+  }
+  {
+    const char input[] = "1E 1000000";
+    char *ptr;
+    long double result;
+    errno = 0;
+    result = strtold (input, &ptr);
+    ASSERT (result == 1.0L);            /* HP-UX 11.31/ia64, IRIX 6.5 */
+    ASSERT (ptr == input + 1);          /* HP-UX 11.31/ia64, IRIX 6.5 */
+    ASSERT (errno == 0);                /* HP-UX 11.31/ia64, IRIX 6.5 */
+  }
+  {
+    const char input[] = "0x1P 1000000";
+    char *ptr;
+    long double result;
+    errno = 0;
+    result = strtold (input, &ptr);
+    ASSERT (result == 1.0L);            /* HP-UX 11.31/ia64, IRIX 6.5, mingw */
+    ASSERT (ptr == input + 3);          /* HP-UX 11.31/ia64, IRIX 6.5, mingw */
+    ASSERT (errno == 0);
+  }
+
+  /* Infinity.  */
+  {
+    const char input[] = "iNf";
+    char *ptr;
+    long double result;
+    errno = 0;
+    result = strtold (input, &ptr);
+    ASSERT (result == HUGE_VALL);       /* IRIX 6.5 */
+    ASSERT (ptr == input + 3);          /* IRIX 6.5 */
+    ASSERT (errno == 0);                /* HP-UX 11.31/ia64 */
+  }
+  {
+    const char input[] = "-InF";
+    char *ptr;
+    long double result;
+    errno = 0;
+    result = strtold (input, &ptr);
+    ASSERT (result == -HUGE_VALL);      /* IRIX 6.5 */
+    ASSERT (ptr == input + 4);          /* IRIX 6.5 */
+    ASSERT (errno == 0);                /* HP-UX 11.31/ia64 */
+  }
+  {
+    const char input[] = "infinite";
+    char *ptr;
+    long double result;
+    errno = 0;
+    result = strtold (input, &ptr);
+    ASSERT (result == HUGE_VALL);       /* IRIX 6.5 */
+    ASSERT (ptr == input + 3);          /* IRIX 6.5 */
+    ASSERT (errno == 0);                /* HP-UX 11.31/ia64 */
+  }
+  {
+    const char input[] = "infinitY";
+    char *ptr;
+    long double result;
+    errno = 0;
+    result = strtold (input, &ptr);
+    ASSERT (result == HUGE_VALL);       /* IRIX 6.5 */
+    ASSERT (ptr == input + 8);          /* IRIX 6.5 */
+    ASSERT (errno == 0);                /* HP-UX 11.31/ia64 */
+  }
+  {
+    const char input[] = "infinitY.";
+    char *ptr;
+    long double result;
+    errno = 0;
+    result = strtold (input, &ptr);
+    ASSERT (result == HUGE_VALL);       /* IRIX 6.5 */
+    ASSERT (ptr == input + 8);          /* IRIX 6.5 */
+    ASSERT (errno == 0);                /* HP-UX 11.31/ia64 */
+  }
+
+  /* NaN.  Some processors set the sign bit of the default NaN, so all
+     we check is that using a sign changes the result.  */
+  {
+    const char input[] = "-nan";
+    char *ptr1;
+    char *ptr2;
+    long double result1;
+    long double result2;
+    errno = 0;
+    result1 = strtold (input, &ptr1);
+    result2 = strtold (input + 1, &ptr2);
+#if 1 /* All known CPUs support NaNs.  */
+    ASSERT (isnanl (result1));          /* IRIX 6.5 */
+    ASSERT (isnanl (result2));          /* IRIX 6.5 */
+# if 0
+    /* Sign bits of NaN is a portability sticking point, not worth
+       worrying about.  */
+    ASSERT (!!signbit (result1) != !!signbit (result2));
+# endif
+    ASSERT (ptr1 == input + 4);         /* IRIX 6.5 */
+    ASSERT (ptr2 == input + 4);         /* IRIX 6.5 */
+    ASSERT (errno == 0);                /* HP-UX 11.31/ia64 */
+#else
+    ASSERT (result1 == 0.0L);
+    ASSERT (result2 == 0.0L);
+    ASSERT (!signbit (result1));
+    ASSERT (!signbit (result2));
+    ASSERT (ptr1 == input);
+    ASSERT (ptr2 == input + 1);
+    ASSERT (errno == 0 || errno == EINVAL);
+#endif
+  }
+  {
+    const char input[] = "+nan(";
+    char *ptr1;
+    char *ptr2;
+    long double result1;
+    long double result2;
+    errno = 0;
+    result1 = strtold (input, &ptr1);
+    result2 = strtold (input + 1, &ptr2);
+#if 1 /* All known CPUs support NaNs.  */
+    ASSERT (isnanl (result1));          /* IRIX 6.5 */
+    ASSERT (isnanl (result2));          /* IRIX 6.5 */
+    ASSERT (!!signbit (result1) == !!signbit (result2));
+    ASSERT (ptr1 == input + 4);         /* Mac OS X 10.5, IRIX 6.5 */
+    ASSERT (ptr2 == input + 4);         /* Mac OS X 10.5, IRIX 6.5 */
+    ASSERT (errno == 0);                /* HP-UX 11.31/ia64 */
+#else
+    ASSERT (result1 == 0.0L);
+    ASSERT (result2 == 0.0L);
+    ASSERT (!signbit (result1));
+    ASSERT (!signbit (result2));
+    ASSERT (ptr1 == input);
+    ASSERT (ptr2 == input + 1);
+    ASSERT (errno == 0 || errno == EINVAL);
+#endif
+  }
+  {
+    const char input[] = "-nan()";
+    char *ptr1;
+    char *ptr2;
+    long double result1;
+    long double result2;
+    errno = 0;
+    result1 = strtold (input, &ptr1);
+    result2 = strtold (input + 1, &ptr2);
+#if 1 /* All known CPUs support NaNs.  */
+    ASSERT (isnanl (result1));          /* IRIX 6.5 */
+    ASSERT (isnanl (result2));          /* IRIX 6.5 */
+# if 0
+    /* Sign bits of NaN is a portability sticking point, not worth
+       worrying about.  */
+    ASSERT (!!signbit (result1) != !!signbit (result2)); /* glibc-2.3.2, musl libc, OpenBSD 6.0, IRIX 6.5, mingw, Haiku */
+# endif
+    ASSERT (ptr1 == input + 6);         /* glibc-2.3.2, IRIX 6.5, mingw, Haiku */
+    ASSERT (ptr2 == input + 6);         /* glibc-2.3.2, IRIX 6.5, mingw, Haiku */
+    ASSERT (errno == 0);                /* HP-UX 11.31/ia64, Haiku */
+#else
+    ASSERT (result1 == 0.0L);
+    ASSERT (result2 == 0.0L);
+    ASSERT (!signbit (result1));
+    ASSERT (!signbit (result2));
+    ASSERT (ptr1 == input);
+    ASSERT (ptr2 == input + 1);
+    ASSERT (errno == 0 || errno == EINVAL);
+#endif
+  }
+  {
+    const char input[] = " nan().";
+    char *ptr;
+    long double result;
+    errno = 0;
+    result = strtold (input, &ptr);
+#if 1 /* All known CPUs support NaNs.  */
+    ASSERT (isnanl (result));           /* IRIX 6.5 */
+    ASSERT (ptr == input + 6);          /* glibc-2.3.2, IRIX 6.5, mingw, Haiku */
+    ASSERT (errno == 0);                /* HP-UX 11.31/ia64, Haiku */
+#else
+    ASSERT (result == 0.0L);
+    ASSERT (!signbit (result));
+    ASSERT (ptr == input);
+    ASSERT (errno == 0 || errno == EINVAL);
+#endif
+  }
+  {
+    /* The behavior of nan(0) is implementation-defined, but all
+       implementations we know of which handle optional
+       n-char-sequences handle nan(0) the same as nan().  */
+    const char input[] = "-nan(0).";
+    char *ptr1;
+    char *ptr2;
+    long double result1;
+    long double result2;
+    errno = 0;
+    result1 = strtold (input, &ptr1);
+    result2 = strtold (input + 1, &ptr2);
+#if 1 /* All known CPUs support NaNs.  */
+    ASSERT (isnanl (result1));          /* IRIX 6.5 */
+    ASSERT (isnanl (result2));          /* IRIX 6.5 */
+# if 0
+    /* Sign bits of NaN is a portability sticking point, not worth
+       worrying about.  */
+    ASSERT (!!signbit (result1) != !!signbit (result2));
+# endif
+    ASSERT (ptr1 == input + 7);         /* glibc-2.3.2, IRIX 6.5, mingw, Haiku */
+    ASSERT (ptr2 == input + 7);         /* glibc-2.3.2, IRIX 6.5, mingw, Haiku */
+    ASSERT (errno == 0);                /* HP-UX 11.31/ia64 */
+#else
+    ASSERT (result1 == 0.0L);
+    ASSERT (result2 == 0.0L);
+    ASSERT (!signbit (result1));
+    ASSERT (!signbit (result2));
+    ASSERT (ptr1 == input);
+    ASSERT (ptr2 == input + 1);
+    ASSERT (errno == 0 || errno == EINVAL);
+#endif
+  }
+
+  /* Hex.  */
+  {
+    const char input[] = "0xa";
+    char *ptr;
+    long double result;
+    errno = 0;
+    result = strtold (input, &ptr);
+    ASSERT (result == 10.0L);           /* IRIX 6.5, mingw */
+    ASSERT (ptr == input + 3);          /* IRIX 6.5, mingw */
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "0XA";
+    char *ptr;
+    long double result;
+    errno = 0;
+    result = strtold (input, &ptr);
+    ASSERT (result == 10.0L);           /* IRIX 6.5, mingw */
+    ASSERT (ptr == input + 3);          /* IRIX 6.5, mingw */
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "0x1p";
+    char *ptr;
+    long double result;
+    errno = 0;
+    result = strtold (input, &ptr);
+    ASSERT (result == 1.0L);            /* IRIX 6.5, mingw */
+    ASSERT (ptr == input + 3);          /* IRIX 6.5, mingw */
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "0x1p+";
+    char *ptr;
+    long double result;
+    errno = 0;
+    result = strtold (input, &ptr);
+    ASSERT (result == 1.0L);            /* IRIX 6.5, mingw */
+    ASSERT (ptr == input + 3);          /* IRIX 6.5, mingw */
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "0x1P+";
+    char *ptr;
+    long double result;
+    errno = 0;
+    result = strtold (input, &ptr);
+    ASSERT (result == 1.0L);            /* IRIX 6.5, mingw */
+    ASSERT (ptr == input + 3);          /* IRIX 6.5, mingw */
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "0x1p+1";
+    char *ptr;
+    long double result;
+    errno = 0;
+    result = strtold (input, &ptr);
+    ASSERT (result == 2.0L);            /* IRIX 6.5, mingw */
+    ASSERT (ptr == input + 6);          /* IRIX 6.5, mingw */
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "0X1P+1";
+    char *ptr;
+    long double result;
+    errno = 0;
+    result = strtold (input, &ptr);
+    ASSERT (result == 2.0L);            /* IRIX 6.5, mingw */
+    ASSERT (ptr == input + 6);          /* IRIX 6.5, mingw */
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "0x1p+1a";
+    char *ptr;
+    long double result;
+    errno = 0;
+    result = strtold (input, &ptr);
+    ASSERT (result == 2.0L);            /* IRIX 6.5, mingw */
+    ASSERT (ptr == input + 6);          /* IRIX 6.5, mingw */
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "0x1p 2";
+    char *ptr;
+    long double result;
+    errno = 0;
+    result = strtold (input, &ptr);
+    ASSERT (result == 1.0L);            /* HP-UX 11.31/ia64, IRIX 6.5, mingw */
+    ASSERT (ptr == input + 3);          /* HP-UX 11.31/ia64, IRIX 6.5, mingw */
+    ASSERT (errno == 0);
+  }
+
+  /* Large buffers.  */
+  {
+    size_t m = 1000000;
+    char *input = malloc (m + 1);
+    if (input)
+      {
+        char *ptr;
+        long double result;
+        memset (input, '\t', m - 1);
+        input[m - 1] = '1';
+        input[m] = '\0';
+        errno = 0;
+        result = strtold (input, &ptr);
+        ASSERT (result == 1.0L);
+        ASSERT (ptr == input + m);
+        ASSERT (errno == 0);
+      }
+    free (input);
+  }
+  {
+    size_t m = 1000000;
+    char *input = malloc (m + 1);
+    if (input)
+      {
+        char *ptr;
+        long double result;
+        memset (input, '0', m - 1);
+        input[m - 1] = '1';
+        input[m] = '\0';
+        errno = 0;
+        result = strtold (input, &ptr);
+        ASSERT (result == 1.0L);
+        ASSERT (ptr == input + m);
+        ASSERT (errno == 0);
+      }
+    free (input);
+  }
+#if 0
+  /* Newlib has an artificial limit of 20000 for the exponent.  TODO -
+     gnulib should fix this.  */
+  {
+    size_t m = 1000000;
+    char *input = malloc (m + 1);
+    if (input)
+      {
+        char *ptr;
+        long double result;
+        input[0] = '.';
+        memset (input + 1, '0', m - 10);
+        input[m - 9] = '1';
+        input[m - 8] = 'e';
+        input[m - 7] = '+';
+        input[m - 6] = '9';
+        input[m - 5] = '9';
+        input[m - 4] = '9';
+        input[m - 3] = '9';
+        input[m - 2] = '9';
+        input[m - 1] = '1';
+        input[m] = '\0';
+        errno = 0;
+        result = strtold (input, &ptr);
+        ASSERT (result == 1.0L);        /* Mac OS X 10.5, FreeBSD 11.0, NetBSD 7.1, OpenBSD 6.0, Minix 3.3, IRIX 6.5, mingw */
+        ASSERT (ptr == input + m);
+        ASSERT (errno == 0);            /* Mac OS X 10.5, FreeBSD 11.0, NetBSD 7.1, OpenBSD 6.0, Minix 3.3, IRIX 6.5, mingw */
+      }
+    free (input);
+  }
+  {
+    size_t m = 1000000;
+    char *input = malloc (m + 1);
+    if (input)
+      {
+        char *ptr;
+        long double result;
+        input[0] = '1';
+        memset (input + 1, '0', m - 9);
+        input[m - 8] = 'e';
+        input[m - 7] = '-';
+        input[m - 6] = '9';
+        input[m - 5] = '9';
+        input[m - 4] = '9';
+        input[m - 3] = '9';
+        input[m - 2] = '9';
+        input[m - 1] = '1';
+        input[m] = '\0';
+        errno = 0;
+        result = strtold (input, &ptr);
+        ASSERT (result == 1.0L);        /* Mac OS X 10.5, FreeBSD 11.0, NetBSD 7.1, OpenBSD 6.0, Minix 3.3, IRIX 6.5, mingw */
+        ASSERT (ptr == input + m);
+        ASSERT (errno == 0);            /* Mac OS X 10.5, FreeBSD 11.0, NetBSD 7.1, OpenBSD 6.0, Minix 3.3, IRIX 6.5, mingw */
+      }
+    free (input);
+  }
+#endif
+  {
+    size_t m = 1000000;
+    char *input = malloc (m + 1);
+    if (input)
+      {
+        char *ptr;
+        long double result;
+        input[0] = '-';
+        input[1] = '0';
+        input[2] = 'e';
+        input[3] = '1';
+        memset (input + 4, '0', m - 3);
+        input[m] = '\0';
+        errno = 0;
+        result = strtold (input, &ptr);
+        ASSERT (result == 0.0L);
+        ASSERT (!!signbit (result) == !!signbit (minus_zerol)); /* IRIX 6.5 */
+        ASSERT (ptr == input + m);
+        ASSERT (errno == 0);
+      }
+    free (input);
+  }
+
+  /* Rounding.  */
+  /* TODO - is it worth some tests of rounding for typical IEEE corner
+     cases, such as .5 ULP rounding up to the smallest denormal and
+     not causing underflow, or LDBL_MIN - .5 ULP not causing an
+     infinite loop?  */
+
+  return status;
+}
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2019-02-02  1:44 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-01-30  3:05 new module 'strtold' Bruno Haible
2019-01-30  5:42 ` Pádraig Brady
2019-02-01  3:34   ` Bruno Haible
2019-02-01 22:47     ` Paul Eggert
2019-02-02  1:44       ` Bruno Haible
2019-02-01  2:16 ` strtod, strtold: various fixes Bruno Haible

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).