In order to understand how xstrtod and xstrtold behave, I added
unit tests, then wrote the specification based on these tests.


2024-07-23  Bruno Haible  <br...@clisp.org>

        xstrtod, xstrtold: Add documentation.
        * lib/xstrtod.h (xstrtod, xstrtold): Add comments.
        * lib/xstrtod.c (XSTRTOD): Improve comments.

2024-07-23  Bruno Haible  <br...@clisp.org>

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

2024-07-23  Bruno Haible  <br...@clisp.org>

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

>From e682de3b59fcc2bb2a2792976ba813c6177baf12 Mon Sep 17 00:00:00 2001
From: Bruno Haible <br...@clisp.org>
Date: Tue, 23 Jul 2024 19:10:34 +0200
Subject: [PATCH 1/3] xstrtod: Add tests.

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

diff --git a/ChangeLog b/ChangeLog
index 73d70911a1..e42f98e9e9 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+2024-07-23  Bruno Haible  <br...@clisp.org>
+
+	xstrtod: Add tests.
+	* tests/test-xstrtod.c: New file, based on tests/test-strtod.h.
+	* modules/xstrtod-tests: New file.
+
 2024-07-23  Bruno Haible  <br...@clisp.org>
 
 	strtof, strtod, strtold tests: Strengthen tests.
diff --git a/modules/xstrtod-tests b/modules/xstrtod-tests
new file mode 100644
index 0000000000..03073cba95
--- /dev/null
+++ b/modules/xstrtod-tests
@@ -0,0 +1,16 @@
+Files:
+tests/test-xstrtod.c
+tests/minus-zero.h
+tests/macros.h
+
+Depends-on:
+strtod
+float
+isnand-nolibm
+signbit
+
+configure.ac:
+
+Makefile.am:
+TESTS += test-xstrtod
+check_PROGRAMS += test-xstrtod
diff --git a/tests/test-xstrtod.c b/tests/test-xstrtod.c
new file mode 100644
index 0000000000..3464120f1e
--- /dev/null
+++ b/tests/test-xstrtod.c
@@ -0,0 +1,1146 @@
+/* Test of xstrtod() in the "C" locale.
+   Copyright (C) 2008-2024 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/>.  */
+
+#include <config.h>
+
+/* Specification.  */
+#include "xstrtod.h"
+
+#include <errno.h>
+#include <float.h>
+#include <math.h>
+#include <string.h>
+
+#include "isnand-nolibm.h"
+#include "minus-zero.h"
+#include "macros.h"
+
+#define UNINIT 0.2614972128476428
+
+int
+main ()
+{
+  /* Subject sequence empty or invalid.  */
+  {
+    const char input[] = "";
+    {
+      const char *ptr;
+      double result = UNINIT;
+      bool ok = xstrtod (input, &ptr, &result, strtod);
+      ASSERT (!ok);
+      ASSERT (result == 0.0);
+      ASSERT (!signbit (result));
+      ASSERT (ptr == input);
+      ASSERT (errno == 0 || errno == EINVAL);
+    }
+    {
+      double result = UNINIT;
+      bool ok = xstrtod (input, NULL, &result, strtod);
+      ASSERT (!ok);
+      ASSERT (result == 0.0);
+      ASSERT (!signbit (result));
+      ASSERT (errno == 0 || errno == EINVAL);
+    }
+  }
+  {
+    const char input[] = " ";
+    {
+      const char *ptr;
+      double result = UNINIT;
+      bool ok = xstrtod (input, &ptr, &result, strtod);
+      ASSERT (!ok);
+      ASSERT (result == 0.0);
+      ASSERT (!signbit (result));
+      ASSERT (ptr == input);
+      ASSERT (errno == 0 || errno == EINVAL);
+    }
+    {
+      double result = UNINIT;
+      bool ok = xstrtod (input, NULL, &result, strtod);
+      ASSERT (!ok);
+      ASSERT (result == 0.0);
+      ASSERT (!signbit (result));
+      ASSERT (errno == 0 || errno == EINVAL);
+    }
+  }
+  {
+    const char input[] = " +";
+    {
+      const char *ptr;
+      double result = UNINIT;
+      bool ok = xstrtod (input, &ptr, &result, strtod);
+      ASSERT (!ok);
+      ASSERT (result == 0.0);
+      ASSERT (!signbit (result));
+      ASSERT (ptr == input);
+      ASSERT (errno == 0 || errno == EINVAL);
+    }
+    {
+      double result = UNINIT;
+      bool ok = xstrtod (input, NULL, &result, strtod);
+      ASSERT (!ok);
+      ASSERT (result == 0.0);
+      ASSERT (!signbit (result));
+      ASSERT (errno == 0 || errno == EINVAL);
+    }
+  }
+  {
+    const char input[] = " .";
+    {
+      const char *ptr;
+      double result = UNINIT;
+      bool ok = xstrtod (input, &ptr, &result, strtod);
+      ASSERT (!ok);
+      ASSERT (result == 0.0);
+      ASSERT (!signbit (result));
+      ASSERT (ptr == input);
+      ASSERT (errno == 0 || errno == EINVAL);
+    }
+    {
+      double result = UNINIT;
+      bool ok = xstrtod (input, NULL, &result, strtod);
+      ASSERT (!ok);
+      ASSERT (result == 0.0);
+      ASSERT (!signbit (result));
+      ASSERT (errno == 0 || errno == EINVAL);
+    }
+  }
+  {
+    const char input[] = " .e0";
+    {
+      const char *ptr;
+      double result = UNINIT;
+      bool ok = xstrtod (input, &ptr, &result, strtod);
+      ASSERT (!ok);
+      ASSERT (result == 0.0);
+      ASSERT (!signbit (result));
+      ASSERT (ptr == input);
+      ASSERT (errno == 0 || errno == EINVAL);
+    }
+    {
+      double result = UNINIT;
+      bool ok = xstrtod (input, NULL, &result, strtod);
+      ASSERT (!ok);
+      ASSERT (result == 0.0);
+      ASSERT (!signbit (result));
+      ASSERT (errno == 0 || errno == EINVAL);
+    }
+  }
+  {
+    const char input[] = " +.e-0";
+    {
+      const char *ptr;
+      double result = UNINIT;
+      bool ok = xstrtod (input, &ptr, &result, strtod);
+      ASSERT (!ok);
+      ASSERT (result == 0.0);
+      ASSERT (!signbit (result));
+      ASSERT (ptr == input);
+      ASSERT (errno == 0 || errno == EINVAL);
+    }
+    {
+      double result = UNINIT;
+      bool ok = xstrtod (input, NULL, &result, strtod);
+      ASSERT (!ok);
+      ASSERT (result == 0.0);
+      ASSERT (!signbit (result));
+      ASSERT (errno == 0 || errno == EINVAL);
+    }
+  }
+  {
+    const char input[] = " in";
+    {
+      const char *ptr;
+      double result = UNINIT;
+      bool ok = xstrtod (input, &ptr, &result, strtod);
+      ASSERT (!ok);
+      ASSERT (result == 0.0);
+      ASSERT (!signbit (result));
+      ASSERT (ptr == input);
+      ASSERT (errno == 0 || errno == EINVAL);
+    }
+    {
+      double result = UNINIT;
+      bool ok = xstrtod (input, NULL, &result, strtod);
+      ASSERT (!ok);
+      ASSERT (result == 0.0);
+      ASSERT (!signbit (result));
+      ASSERT (errno == 0 || errno == EINVAL);
+    }
+  }
+  {
+    const char input[] = " na";
+    {
+      const char *ptr;
+      double result = UNINIT;
+      bool ok = xstrtod (input, &ptr, &result, strtod);
+      ASSERT (!ok);
+      ASSERT (result == 0.0);
+      ASSERT (!signbit (result));
+      ASSERT (ptr == input);
+      ASSERT (errno == 0 || errno == EINVAL);
+    }
+    {
+      double result = UNINIT;
+      bool ok = xstrtod (input, NULL, &result, strtod);
+      ASSERT (!ok);
+      ASSERT (result == 0.0);
+      ASSERT (!signbit (result));
+      ASSERT (errno == 0 || errno == EINVAL);
+    }
+  }
+
+  /* Simple floating point values.  */
+  {
+    const char input[] = "1";
+    const char *ptr;
+    double result = UNINIT;
+    bool ok = xstrtod (input, &ptr, &result, strtod);
+    ASSERT (ok);
+    ASSERT (result == 1.0);
+    ASSERT (ptr == input + 1);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "1.";
+    const char *ptr;
+    double result = UNINIT;
+    bool ok = xstrtod (input, &ptr, &result, strtod);
+    ASSERT (ok);
+    ASSERT (result == 1.0);
+    ASSERT (ptr == input + 2);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = ".5";
+    const char *ptr;
+    double result = UNINIT;
+    bool ok = xstrtod (input, &ptr, &result, strtod);
+    ASSERT (ok);
+    ASSERT (result == 0.5);
+    ASSERT (ptr == input + 2);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = " 1";
+    {
+      const char *ptr;
+      double result = UNINIT;
+      bool ok = xstrtod (input, &ptr, &result, strtod);
+      ASSERT (ok);
+      ASSERT (result == 1.0);
+      ASSERT (ptr == input + 2);
+      ASSERT (errno == 0);
+    }
+    {
+      double result = UNINIT;
+      bool ok = xstrtod (input, NULL, &result, strtod);
+      ASSERT (ok);
+      ASSERT (result == 1.0);
+      ASSERT (errno == 0);
+    }
+  }
+  {
+    const char input[] = "+1";
+    const char *ptr;
+    double result = UNINIT;
+    bool ok = xstrtod (input, &ptr, &result, strtod);
+    ASSERT (ok);
+    ASSERT (result == 1.0);
+    ASSERT (ptr == input + 2);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "-1";
+    const char *ptr;
+    double result = UNINIT;
+    bool ok = xstrtod (input, &ptr, &result, strtod);
+    ASSERT (ok);
+    ASSERT (result == -1.0);
+    ASSERT (ptr == input + 2);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "1e0";
+    const char *ptr;
+    double result = UNINIT;
+    bool ok = xstrtod (input, &ptr, &result, strtod);
+    ASSERT (ok);
+    ASSERT (result == 1.0);
+    ASSERT (ptr == input + 3);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "1e+0";
+    const char *ptr;
+    double result = UNINIT;
+    bool ok = xstrtod (input, &ptr, &result, strtod);
+    ASSERT (ok);
+    ASSERT (result == 1.0);
+    ASSERT (ptr == input + 4);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "1e-0";
+    const char *ptr;
+    double result = UNINIT;
+    bool ok = xstrtod (input, &ptr, &result, strtod);
+    ASSERT (ok);
+    ASSERT (result == 1.0);
+    ASSERT (ptr == input + 4);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "1e1";
+    const char *ptr;
+    double result = UNINIT;
+    bool ok = xstrtod (input, &ptr, &result, strtod);
+    ASSERT (ok);
+    ASSERT (result == 10.0);
+    ASSERT (ptr == input + 3);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "5e-1";
+    const char *ptr;
+    double result = UNINIT;
+    bool ok = xstrtod (input, &ptr, &result, strtod);
+    ASSERT (ok);
+    ASSERT (result == 0.5);
+    ASSERT (ptr == input + 4);
+    ASSERT (errno == 0);
+  }
+
+  /* Zero.  */
+  {
+    const char input[] = "0";
+    const char *ptr;
+    double result = UNINIT;
+    bool ok = xstrtod (input, &ptr, &result, strtod);
+    ASSERT (ok);
+    ASSERT (result == 0.0);
+    ASSERT (!signbit (result));
+    ASSERT (ptr == input + 1);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = ".0";
+    const char *ptr;
+    double result = UNINIT;
+    bool ok = xstrtod (input, &ptr, &result, strtod);
+    ASSERT (ok);
+    ASSERT (result == 0.0);
+    ASSERT (!signbit (result));
+    ASSERT (ptr == input + 2);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "0e0";
+    const char *ptr;
+    double result = UNINIT;
+    bool ok = xstrtod (input, &ptr, &result, strtod);
+    ASSERT (ok);
+    ASSERT (result == 0.0);
+    ASSERT (!signbit (result));
+    ASSERT (ptr == input + 3);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "0e+9999999";
+    const char *ptr;
+    double result = UNINIT;
+    bool ok = xstrtod (input, &ptr, &result, strtod);
+    ASSERT (ok);
+    ASSERT (result == 0.0);
+    ASSERT (!signbit (result));
+    ASSERT (ptr == input + 10);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "0e-9999999";
+    const char *ptr;
+    double result = UNINIT;
+    bool ok = xstrtod (input, &ptr, &result, strtod);
+    ASSERT (ok);
+    ASSERT (result == 0.0);
+    ASSERT (!signbit (result));
+    ASSERT (ptr == input + 10);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "-0";
+    const char *ptr;
+    double result = UNINIT;
+    bool ok = xstrtod (input, &ptr, &result, strtod);
+    ASSERT (ok);
+    ASSERT (result == 0.0);
+    ASSERT (!!signbit (result) == !!signbit (minus_zerod));
+    ASSERT (ptr == input + 2);
+    ASSERT (errno == 0);
+  }
+
+  /* Suffixes.  */
+  {
+    const char input[] = "1f";
+    const char *ptr;
+    double result = UNINIT;
+    bool ok = xstrtod (input, &ptr, &result, strtod);
+    ASSERT (ok);
+    ASSERT (result == 1.0);
+    ASSERT (ptr == input + 1);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "1.f";
+    const char *ptr;
+    double result = UNINIT;
+    bool ok = xstrtod (input, &ptr, &result, strtod);
+    ASSERT (ok);
+    ASSERT (result == 1.0);
+    ASSERT (ptr == input + 2);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "1e";
+    {
+      const char *ptr;
+      double result = UNINIT;
+      bool ok = xstrtod (input, &ptr, &result, strtod);
+      ASSERT (ok);
+      ASSERT (result == 1.0);
+      ASSERT (ptr == input + 1);
+      ASSERT (errno == 0);
+    }
+    {
+      double result = UNINIT;
+      bool ok = xstrtod (input, NULL, &result, strtod);
+      ASSERT (!ok);
+      ASSERT (result == 1.0);
+      ASSERT (errno == 0);
+    }
+  }
+  {
+    const char input[] = "1e+";
+    {
+      const char *ptr;
+      double result = UNINIT;
+      bool ok = xstrtod (input, &ptr, &result, strtod);
+      ASSERT (ok);
+      ASSERT (result == 1.0);
+      ASSERT (ptr == input + 1);
+      ASSERT (errno == 0);
+    }
+    {
+      double result = UNINIT;
+      bool ok = xstrtod (input, NULL, &result, strtod);
+      ASSERT (!ok);
+      ASSERT (result == 1.0);
+      ASSERT (errno == 0);
+    }
+  }
+  {
+    const char input[] = "1e-";
+    {
+      const char *ptr;
+      double result = UNINIT;
+      bool ok = xstrtod (input, &ptr, &result, strtod);
+      ASSERT (ok);
+      ASSERT (result == 1.0);
+      ASSERT (ptr == input + 1);
+      ASSERT (errno == 0);
+    }
+    {
+      double result = UNINIT;
+      bool ok = xstrtod (input, NULL, &result, strtod);
+      ASSERT (!ok);
+      ASSERT (result == 1.0);
+      ASSERT (errno == 0);
+    }
+  }
+  {
+    const char input[] = "1E 2";
+    {
+      const char *ptr;
+      double result = UNINIT;
+      bool ok = xstrtod (input, &ptr, &result, strtod);
+      ASSERT (ok);
+      ASSERT (result == 1.0);
+      ASSERT (ptr == input + 1);
+      ASSERT (errno == 0);
+    }
+    {
+      double result = UNINIT;
+      bool ok = xstrtod (input, NULL, &result, strtod);
+      ASSERT (!ok);
+      ASSERT (result == 1.0);
+      ASSERT (errno == 0);
+    }
+  }
+  {
+    const char input[] = "0x";
+    const char *ptr;
+    double result = UNINIT;
+    bool ok = xstrtod (input, &ptr, &result, strtod);
+    ASSERT (ok);
+    ASSERT (result == 0.0);
+    ASSERT (!signbit (result));
+    ASSERT (ptr == input + 1);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "00x1";
+    const char *ptr;
+    double result = UNINIT;
+    bool ok = xstrtod (input, &ptr, &result, strtod);
+    ASSERT (ok);
+    ASSERT (result == 0.0);
+    ASSERT (!signbit (result));
+    ASSERT (ptr == input + 2);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "-0x";
+    const char *ptr;
+    double result = UNINIT;
+    bool ok = xstrtod (input, &ptr, &result, strtod);
+    ASSERT (ok);
+    ASSERT (result == 0.0);
+    ASSERT (!!signbit (result) == !!signbit (minus_zerod));
+    ASSERT (ptr == input + 2);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "0xg";
+    const char *ptr;
+    double result = UNINIT;
+    bool ok = xstrtod (input, &ptr, &result, strtod);
+    ASSERT (ok);
+    ASSERT (result == 0.0);
+    ASSERT (!signbit (result));
+    ASSERT (ptr == input + 1);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "0xp";
+    const char *ptr;
+    double result = UNINIT;
+    bool ok = xstrtod (input, &ptr, &result, strtod);
+    ASSERT (ok);
+    ASSERT (result == 0.0);
+    ASSERT (!signbit (result));
+    ASSERT (ptr == input + 1);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "0XP";
+    const char *ptr;
+    double result = UNINIT;
+    bool ok = xstrtod (input, &ptr, &result, strtod);
+    ASSERT (ok);
+    ASSERT (result == 0.0);
+    ASSERT (!signbit (result));
+    ASSERT (ptr == input + 1);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "0x.";
+    const char *ptr;
+    double result = UNINIT;
+    bool ok = xstrtod (input, &ptr, &result, strtod);
+    ASSERT (ok);
+    ASSERT (result == 0.0);
+    ASSERT (!signbit (result));
+    ASSERT (ptr == input + 1);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "0xp+";
+    const char *ptr;
+    double result = UNINIT;
+    bool ok = xstrtod (input, &ptr, &result, strtod);
+    ASSERT (ok);
+    ASSERT (result == 0.0);
+    ASSERT (!signbit (result));
+    ASSERT (ptr == input + 1);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "0xp+1";
+    const char *ptr;
+    double result = UNINIT;
+    bool ok = xstrtod (input, &ptr, &result, strtod);
+    ASSERT (ok);
+    ASSERT (result == 0.0);
+    ASSERT (!signbit (result));
+    ASSERT (ptr == input + 1);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "0x.p+1";
+    const char *ptr;
+    double result = UNINIT;
+    bool ok = xstrtod (input, &ptr, &result, strtod);
+    ASSERT (ok);
+    ASSERT (result == 0.0);
+    ASSERT (!signbit (result));
+    ASSERT (ptr == input + 1);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "1p+1";
+    const char *ptr;
+    double result = UNINIT;
+    bool ok = xstrtod (input, &ptr, &result, strtod);
+    ASSERT (ok);
+    ASSERT (result == 1.0);
+    ASSERT (ptr == input + 1);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "1P+1";
+    const char *ptr;
+    double result = UNINIT;
+    bool ok = xstrtod (input, &ptr, &result, strtod);
+    ASSERT (ok);
+    ASSERT (result == 1.0);
+    ASSERT (ptr == input + 1);
+    ASSERT (errno == 0);
+  }
+
+  /* Overflow.  */
+  {
+    const char input[] = "1e500";
+    const char *ptr;
+    double result = UNINIT;
+    bool ok = xstrtod (input, &ptr, &result, strtod);
+    ASSERT (!ok);
+    ASSERT (result == HUGE_VAL);
+    ASSERT (ptr == input + 5);
+    ASSERT (errno == ERANGE);
+  }
+  {
+    const char input[] = "1E1000000";
+    const char *ptr;
+    double result = UNINIT;
+    bool ok = xstrtod (input, &ptr, &result, strtod);
+    ASSERT (!ok);
+    ASSERT (result == HUGE_VAL);
+    ASSERT (ptr == input + 9);
+    ASSERT (errno == ERANGE);
+  }
+  {
+    const char input[] = "-1E1000000";
+    const char *ptr;
+    double result = UNINIT;
+    bool ok = xstrtod (input, &ptr, &result, strtod);
+    ASSERT (!ok);
+    ASSERT (result == -HUGE_VAL);
+    ASSERT (ptr == input + 10);
+    ASSERT (errno == ERANGE);
+  }
+
+  /* Gradual underflow, resulting in a denormalized number.  */
+  {
+    const char input[] = "1e-320";
+    const char *ptr;
+    double result = UNINIT;
+    bool ok = xstrtod (input, &ptr, &result, strtod);
+#if defined _MSC_VER
+    ASSERT (ok);
+#else
+    ASSERT (!ok);
+#endif
+    ASSERT (0.0 < result && result <= DBL_MIN);
+    ASSERT (ptr == input + 6);
+#if defined _MSC_VER
+    ASSERT (errno == 0);
+#else
+    ASSERT (errno == ERANGE);
+#endif
+  }
+  {
+    const char input[] = "-1e-320";
+    const char *ptr;
+    double result = UNINIT;
+    bool ok = xstrtod (input, &ptr, &result, strtod);
+#if defined _MSC_VER
+    ASSERT (ok);
+#else
+    ASSERT (!ok);
+#endif
+    ASSERT (-DBL_MIN <= result && result < 0.0);
+    ASSERT (ptr == input + 7);
+#if defined _MSC_VER
+    ASSERT (errno == 0);
+#else
+    ASSERT (errno == ERANGE);
+#endif
+  }
+
+  /* Flush-to-zero underflow.  */
+  {
+    const char input[] = "1E-100000";
+    const char *ptr;
+    double result = UNINIT;
+    bool ok = xstrtod (input, &ptr, &result, strtod);
+    ASSERT (ok);
+    ASSERT (result == 0.0);
+    ASSERT (!signbit (result));
+    ASSERT (ptr == input + 9);
+    ASSERT (errno == ERANGE);
+  }
+  {
+    const char input[] = "-1E-100000";
+    const char *ptr;
+    double result = UNINIT;
+    bool ok = xstrtod (input, &ptr, &result, strtod);
+    ASSERT (ok);
+    ASSERT (result == 0.0);
+    /* Negative underflow.  Expect a negative sign, although POSIX allows +0.0.
+       See also <https://sourceware.org/bugzilla/show_bug.cgi?id=5995>.  */
+    ASSERT (!!signbit (result) == !!signbit (minus_zerod));
+    ASSERT (ptr == input + 10);
+    ASSERT (errno == ERANGE);
+  }
+
+  /* Space before the exponent.  */
+  {
+    const char input[] = "1E 1000000";
+    const char *ptr;
+    double result = UNINIT;
+    bool ok = xstrtod (input, &ptr, &result, strtod);
+    ASSERT (ok);
+    ASSERT (result == 1.0);
+    ASSERT (ptr == input + 1);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "0x1P 1000000";
+    const char *ptr;
+    double result = UNINIT;
+    bool ok = xstrtod (input, &ptr, &result, strtod);
+    ASSERT (ok);
+    ASSERT (result == 1.0);
+    ASSERT (ptr == input + 3);
+    ASSERT (errno == 0);
+  }
+
+  /* Infinity.  */
+  {
+    const char input[] = "iNf";
+    const char *ptr;
+    double result = UNINIT;
+    bool ok = xstrtod (input, &ptr, &result, strtod);
+    ASSERT (ok);
+    ASSERT (result == HUGE_VAL);
+    ASSERT (ptr == input + 3);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "-InF";
+    const char *ptr;
+    double result = UNINIT;
+    bool ok = xstrtod (input, &ptr, &result, strtod);
+    ASSERT (ok);
+    ASSERT (result == -HUGE_VAL);
+    ASSERT (ptr == input + 4);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "infinite";
+    const char *ptr;
+    double result = UNINIT;
+    bool ok = xstrtod (input, &ptr, &result, strtod);
+    ASSERT (ok);
+    ASSERT (result == HUGE_VAL);
+    ASSERT (ptr == input + 3);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "infinitY";
+    const char *ptr;
+    double result = UNINIT;
+    bool ok = xstrtod (input, &ptr, &result, strtod);
+    ASSERT (ok);
+    ASSERT (result == HUGE_VAL);
+    ASSERT (ptr == input + 8);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "infinitY.";
+    const char *ptr;
+    double result = UNINIT;
+    bool ok = xstrtod (input, &ptr, &result, strtod);
+    ASSERT (ok);
+    ASSERT (result == HUGE_VAL);
+    ASSERT (ptr == input + 8);
+    ASSERT (errno == 0);
+  }
+
+  /* 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";
+    const char *ptr1;
+    const char *ptr2;
+    double result1 = UNINIT;
+    double result2 = UNINIT;
+    bool ok1 = xstrtod (input, &ptr1, &result1, strtod);
+    bool ok2 = xstrtod (input + 1, &ptr2, &result2, strtod);
+    ASSERT (ok1);
+    ASSERT (ok2);
+#if 1 /* All known CPUs support NaNs.  */
+    ASSERT (isnand (result1));
+    ASSERT (isnand (result2));
+# 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);
+    ASSERT (ptr2 == input + 4);
+    ASSERT (errno == 0);
+#else
+    ASSERT (result1 == 0.0);
+    ASSERT (result2 == 0.0);
+    ASSERT (!signbit (result1));
+    ASSERT (!signbit (result2));
+    ASSERT (ptr1 == input);
+    ASSERT (ptr2 == input + 1);
+    ASSERT (errno == 0 || errno == EINVAL);
+#endif
+  }
+  {
+    const char input[] = "+nan(";
+    const char *ptr1;
+    const char *ptr2;
+    double result1 = UNINIT;
+    double result2 = UNINIT;
+    bool ok1 = xstrtod (input, &ptr1, &result1, strtod);
+    bool ok2 = xstrtod (input + 1, &ptr2, &result2, strtod);
+    ASSERT (ok1);
+    ASSERT (ok2);
+#if 1 /* All known CPUs support NaNs.  */
+    ASSERT (isnand (result1));
+    ASSERT (isnand (result2));
+    ASSERT (!!signbit (result1) == !!signbit (result2));
+    ASSERT (ptr1 == input + 4);
+    ASSERT (ptr2 == input + 4);
+    ASSERT (errno == 0);
+#else
+    ASSERT (result1 == 0.0);
+    ASSERT (result2 == 0.0);
+    ASSERT (!signbit (result1));
+    ASSERT (!signbit (result2));
+    ASSERT (ptr1 == input);
+    ASSERT (ptr2 == input + 1);
+    ASSERT (errno == 0 || errno == EINVAL);
+#endif
+  }
+  {
+    const char input[] = "-nan()";
+    const char *ptr1;
+    const char *ptr2;
+    double result1 = UNINIT;
+    double result2 = UNINIT;
+    bool ok1 = xstrtod (input, &ptr1, &result1, strtod);
+    bool ok2 = xstrtod (input + 1, &ptr2, &result2, strtod);
+    ASSERT (ok1);
+    ASSERT (ok2);
+#if 1 /* All known CPUs support NaNs.  */
+    ASSERT (isnand (result1));
+    ASSERT (isnand (result2));
+# if 0
+    /* Sign bits of NaN is a portability sticking point, not worth
+       worrying about.  */
+    ASSERT (!!signbit (result1) != !!signbit (result2));
+# endif
+    ASSERT (ptr1 == input + 6);
+    ASSERT (ptr2 == input + 6);
+    ASSERT (errno == 0);
+#else
+    ASSERT (result1 == 0.0);
+    ASSERT (result2 == 0.0);
+    ASSERT (!signbit (result1));
+    ASSERT (!signbit (result2));
+    ASSERT (ptr1 == input);
+    ASSERT (ptr2 == input + 1);
+    ASSERT (errno == 0 || errno == EINVAL);
+#endif
+  }
+  {
+    const char input[] = " nan().";
+    const char *ptr;
+    double result = UNINIT;
+    bool ok = xstrtod (input, &ptr, &result, strtod);
+    ASSERT (ok);
+#if 1 /* All known CPUs support NaNs.  */
+    ASSERT (isnand (result));
+    ASSERT (ptr == input + 6);
+    ASSERT (errno == 0);
+#else
+    ASSERT (result == 0.0);
+    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).";
+    const char *ptr1;
+    const char *ptr2;
+    double result1 = UNINIT;
+    double result2 = UNINIT;
+    bool ok1 = xstrtod (input, &ptr1, &result1, strtod);
+    bool ok2 = xstrtod (input + 1, &ptr2, &result2, strtod);
+    ASSERT (ok1);
+    ASSERT (ok2);
+#if 1 /* All known CPUs support NaNs.  */
+    ASSERT (isnand (result1));
+    ASSERT (isnand (result2));
+# 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);
+    ASSERT (ptr2 == input + 7);
+    ASSERT (errno == 0);
+#else
+    ASSERT (result1 == 0.0);
+    ASSERT (result2 == 0.0);
+    ASSERT (!signbit (result1));
+    ASSERT (!signbit (result2));
+    ASSERT (ptr1 == input);
+    ASSERT (ptr2 == input + 1);
+    ASSERT (errno == 0 || errno == EINVAL);
+#endif
+  }
+
+  /* Hex.  */
+  {
+    const char input[] = "0xa";
+    const char *ptr;
+    double result = UNINIT;
+    bool ok = xstrtod (input, &ptr, &result, strtod);
+    ASSERT (ok);
+    ASSERT (result == 10.0);
+    ASSERT (ptr == input + 3);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "0XA";
+    const char *ptr;
+    double result = UNINIT;
+    bool ok = xstrtod (input, &ptr, &result, strtod);
+    ASSERT (ok);
+    ASSERT (result == 10.0);
+    ASSERT (ptr == input + 3);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "0x1p";
+    const char *ptr;
+    double result = UNINIT;
+    bool ok = xstrtod (input, &ptr, &result, strtod);
+    ASSERT (ok);
+    ASSERT (result == 1.0);
+    ASSERT (ptr == input + 3);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "0x1p+";
+    const char *ptr;
+    double result = UNINIT;
+    bool ok = xstrtod (input, &ptr, &result, strtod);
+    ASSERT (ok);
+    ASSERT (result == 1.0);
+    ASSERT (ptr == input + 3);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "0x1P+";
+    const char *ptr;
+    double result = UNINIT;
+    bool ok = xstrtod (input, &ptr, &result, strtod);
+    ASSERT (ok);
+    ASSERT (result == 1.0);
+    ASSERT (ptr == input + 3);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "0x1p+1";
+    const char *ptr;
+    double result = UNINIT;
+    bool ok = xstrtod (input, &ptr, &result, strtod);
+    ASSERT (ok);
+    ASSERT (result == 2.0);
+    ASSERT (ptr == input + 6);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "0X1P+1";
+    const char *ptr;
+    double result = UNINIT;
+    bool ok = xstrtod (input, &ptr, &result, strtod);
+    ASSERT (ok);
+    ASSERT (result == 2.0);
+    ASSERT (ptr == input + 6);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "0x1p+1a";
+    const char *ptr;
+    double result = UNINIT;
+    bool ok = xstrtod (input, &ptr, &result, strtod);
+    ASSERT (ok);
+    ASSERT (result == 2.0);
+    ASSERT (ptr == input + 6);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "0x1p 2";
+    const char *ptr;
+    double result = UNINIT;
+    bool ok = xstrtod (input, &ptr, &result, strtod);
+    ASSERT (ok);
+    ASSERT (result == 1.0);
+    ASSERT (ptr == input + 3);
+    ASSERT (errno == 0);
+  }
+
+  /* Large buffers.  */
+  {
+    size_t m = 1000000;
+    char *input = malloc (m + 1);
+    if (input)
+      {
+        const char *ptr;
+        double result = UNINIT;
+        memset (input, '\t', m - 1);
+        input[m - 1] = '1';
+        input[m] = '\0';
+        bool ok = xstrtod (input, &ptr, &result, strtod);
+        ASSERT (ok);
+        ASSERT (result == 1.0);
+        ASSERT (ptr == input + m);
+        ASSERT (errno == 0);
+      }
+    free (input);
+  }
+  {
+    size_t m = 1000000;
+    char *input = malloc (m + 1);
+    if (input)
+      {
+        const char *ptr;
+        double result = UNINIT;
+        memset (input, '0', m - 1);
+        input[m - 1] = '1';
+        input[m] = '\0';
+        bool ok = xstrtod (input, &ptr, &result, strtod);
+        ASSERT (ok);
+        ASSERT (result == 1.0);
+        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)
+      {
+        const char *ptr;
+        double result = UNINIT;
+        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';
+        bool ok = xstrtod (input, &ptr, &result, strtod);
+        ASSERT (ok);
+        ASSERT (result == 1.0);
+        ASSERT (ptr == input + m);
+        ASSERT (errno == 0);
+      }
+    free (input);
+  }
+  {
+    size_t m = 1000000;
+    char *input = malloc (m + 1);
+    if (input)
+      {
+        const char *ptr;
+        double result = UNINIT;
+        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';
+        bool ok = xstrtod (input, &ptr, &result, strtod);
+        ASSERT (ok);
+        ASSERT (result == 1.0);
+        ASSERT (ptr == input + m);
+        ASSERT (errno == 0);
+      }
+    free (input);
+  }
+#endif
+  {
+    size_t m = 1000000;
+    char *input = malloc (m + 1);
+    if (input)
+      {
+        const char *ptr;
+        double result = UNINIT;
+        input[0] = '-';
+        input[1] = '0';
+        input[2] = 'e';
+        input[3] = '1';
+        memset (input + 4, '0', m - 3);
+        input[m] = '\0';
+        bool ok = xstrtod (input, &ptr, &result, strtod);
+        ASSERT (ok);
+        ASSERT (result == 0.0);
+        ASSERT (!!signbit (result) == !!signbit (minus_zerod));
+        ASSERT (ptr == input + m);
+        ASSERT (errno == 0);
+      }
+    free (input);
+  }
+
+  return test_exit_status;
+}
-- 
2.34.1

>From 3e73aad195ce45a876a7657d31618abcb68bdaa8 Mon Sep 17 00:00:00 2001
From: Bruno Haible <br...@clisp.org>
Date: Tue, 23 Jul 2024 19:11:00 +0200
Subject: [PATCH 2/3] xstrtold: Add tests.

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

diff --git a/ChangeLog b/ChangeLog
index e42f98e9e9..890cd57c2f 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+2024-07-23  Bruno Haible  <br...@clisp.org>
+
+	xstrtold: Add tests.
+	* tests/test-xstrtold.c: New file, based on tests/test-strtold.h.
+	* modules/xstrtold-tests: New file.
+
 2024-07-23  Bruno Haible  <br...@clisp.org>
 
 	xstrtod: Add tests.
diff --git a/modules/xstrtold-tests b/modules/xstrtold-tests
new file mode 100644
index 0000000000..57f25b9559
--- /dev/null
+++ b/modules/xstrtold-tests
@@ -0,0 +1,16 @@
+Files:
+tests/test-xstrtold.c
+tests/minus-zero.h
+tests/macros.h
+
+Depends-on:
+strtold
+float
+isnanl-nolibm
+signbit
+
+configure.ac:
+
+Makefile.am:
+TESTS += test-xstrtold
+check_PROGRAMS += test-xstrtold
diff --git a/tests/test-xstrtold.c b/tests/test-xstrtold.c
new file mode 100644
index 0000000000..b83e78067b
--- /dev/null
+++ b/tests/test-xstrtold.c
@@ -0,0 +1,1154 @@
+/* Test of xstrtold() in the "C" locale.
+   Copyright (C) 2008-2024 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/>.  */
+
+#include <config.h>
+
+/* Specification.  */
+#include "xstrtod.h"
+
+#include <errno.h>
+#include <float.h>
+#include <math.h>
+#include <string.h>
+
+#include "isnanl-nolibm.h"
+#include "minus-zero.h"
+#include "macros.h"
+
+#define UNINIT 0.26149721284764278375542683860869586L
+
+int
+main ()
+{
+  /* Subject sequence empty or invalid.  */
+  {
+    const char input[] = "";
+    {
+      const char *ptr;
+      long double result = UNINIT;
+      bool ok = xstrtold (input, &ptr, &result, strtold);
+      ASSERT (!ok);
+      ASSERT (result == 0.0L);
+      ASSERT (!signbit (result));
+      ASSERT (ptr == input);
+      ASSERT (errno == 0 || errno == EINVAL);
+    }
+    {
+      long double result = UNINIT;
+      bool ok = xstrtold (input, NULL, &result, strtold);
+      ASSERT (!ok);
+      ASSERT (result == 0.0L);
+      ASSERT (!signbit (result));
+      ASSERT (errno == 0 || errno == EINVAL);
+    }
+  }
+  {
+    const char input[] = " ";
+    {
+      const char *ptr;
+      long double result = UNINIT;
+      bool ok = xstrtold (input, &ptr, &result, strtold);
+      ASSERT (!ok);
+      ASSERT (result == 0.0L);
+      ASSERT (!signbit (result));
+      ASSERT (ptr == input);
+      ASSERT (errno == 0 || errno == EINVAL);
+    }
+    {
+      long double result = UNINIT;
+      bool ok = xstrtold (input, NULL, &result, strtold);
+      ASSERT (!ok);
+      ASSERT (result == 0.0L);
+      ASSERT (!signbit (result));
+      ASSERT (errno == 0 || errno == EINVAL);
+    }
+  }
+  {
+    const char input[] = " +";
+    {
+      const char *ptr;
+      long double result = UNINIT;
+      bool ok = xstrtold (input, &ptr, &result, strtold);
+      ASSERT (!ok);
+      ASSERT (result == 0.0L);
+      ASSERT (!signbit (result));
+      ASSERT (ptr == input);
+      ASSERT (errno == 0 || errno == EINVAL);
+    }
+    {
+      long double result = UNINIT;
+      bool ok = xstrtold (input, NULL, &result, strtold);
+      ASSERT (!ok);
+      ASSERT (result == 0.0L);
+      ASSERT (!signbit (result));
+      ASSERT (errno == 0 || errno == EINVAL);
+    }
+  }
+  {
+    const char input[] = " .";
+    {
+      const char *ptr;
+      long double result = UNINIT;
+      bool ok = xstrtold (input, &ptr, &result, strtold);
+      ASSERT (!ok);
+      ASSERT (result == 0.0L);
+      ASSERT (!signbit (result));
+      ASSERT (ptr == input);
+      ASSERT (errno == 0 || errno == EINVAL);
+    }
+    {
+      long double result = UNINIT;
+      bool ok = xstrtold (input, NULL, &result, strtold);
+      ASSERT (!ok);
+      ASSERT (result == 0.0L);
+      ASSERT (!signbit (result));
+      ASSERT (errno == 0 || errno == EINVAL);
+    }
+  }
+  {
+    const char input[] = " .e0";
+    {
+      const char *ptr;
+      long double result = UNINIT;
+      bool ok = xstrtold (input, &ptr, &result, strtold);
+      ASSERT (!ok);
+      ASSERT (result == 0.0L);
+      ASSERT (!signbit (result));
+      ASSERT (ptr == input);
+      ASSERT (errno == 0 || errno == EINVAL);
+    }
+    {
+      long double result = UNINIT;
+      bool ok = xstrtold (input, NULL, &result, strtold);
+      ASSERT (!ok);
+      ASSERT (result == 0.0L);
+      ASSERT (!signbit (result));
+      ASSERT (errno == 0 || errno == EINVAL);
+    }
+  }
+  {
+    const char input[] = " +.e-0";
+    {
+      const char *ptr;
+      long double result = UNINIT;
+      bool ok = xstrtold (input, &ptr, &result, strtold);
+      ASSERT (!ok);
+      ASSERT (result == 0.0L);
+      ASSERT (!signbit (result));
+      ASSERT (ptr == input);
+      ASSERT (errno == 0 || errno == EINVAL);
+    }
+    {
+      long double result = UNINIT;
+      bool ok = xstrtold (input, NULL, &result, strtold);
+      ASSERT (!ok);
+      ASSERT (result == 0.0L);
+      ASSERT (!signbit (result));
+      ASSERT (errno == 0 || errno == EINVAL);
+    }
+  }
+  {
+    const char input[] = " in";
+    {
+      const char *ptr;
+      long double result = UNINIT;
+      bool ok = xstrtold (input, &ptr, &result, strtold);
+      ASSERT (!ok);
+      ASSERT (result == 0.0L);
+      ASSERT (!signbit (result));
+      ASSERT (ptr == input);
+      ASSERT (errno == 0 || errno == EINVAL);
+    }
+    {
+      long double result = UNINIT;
+      bool ok = xstrtold (input, NULL, &result, strtold);
+      ASSERT (!ok);
+      ASSERT (result == 0.0L);
+      ASSERT (!signbit (result));
+      ASSERT (errno == 0 || errno == EINVAL);
+    }
+  }
+  {
+    const char input[] = " na";
+    {
+      const char *ptr;
+      long double result = UNINIT;
+      bool ok = xstrtold (input, &ptr, &result, strtold);
+      ASSERT (!ok);
+      ASSERT (result == 0.0L);
+      ASSERT (!signbit (result));
+      ASSERT (ptr == input);
+      ASSERT (errno == 0 || errno == EINVAL);
+    }
+    {
+      long double result = UNINIT;
+      bool ok = xstrtold (input, NULL, &result, strtold);
+      ASSERT (!ok);
+      ASSERT (result == 0.0L);
+      ASSERT (!signbit (result));
+      ASSERT (errno == 0 || errno == EINVAL);
+    }
+  }
+
+  /* Simple floating point values.  */
+  {
+    const char input[] = "1";
+    const char *ptr;
+    long double result = UNINIT;
+    bool ok = xstrtold (input, &ptr, &result, strtold);
+    ASSERT (ok);
+    ASSERT (result == 1.0L);
+    ASSERT (ptr == input + 1);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "1.";
+    const char *ptr;
+    long double result = UNINIT;
+    bool ok = xstrtold (input, &ptr, &result, strtold);
+    ASSERT (ok);
+    ASSERT (result == 1.0L);
+    ASSERT (ptr == input + 2);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = ".5";
+    const char *ptr;
+    long double result = UNINIT;
+    bool ok = xstrtold (input, &ptr, &result, strtold);
+    ASSERT (ok);
+    ASSERT (result == 0.5L);
+    ASSERT (ptr == input + 2);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = " 1";
+    {
+      const char *ptr;
+      long double result = UNINIT;
+      bool ok = xstrtold (input, &ptr, &result, strtold);
+      ASSERT (ok);
+      ASSERT (result == 1.0L);
+      ASSERT (ptr == input + 2);
+      ASSERT (errno == 0);
+    }
+    {
+      long double result = UNINIT;
+      bool ok = xstrtold (input, NULL, &result, strtold);
+      ASSERT (ok);
+      ASSERT (result == 1.0L);
+      ASSERT (errno == 0);
+    }
+  }
+  {
+    const char input[] = "+1";
+    const char *ptr;
+    long double result = UNINIT;
+    bool ok = xstrtold (input, &ptr, &result, strtold);
+    ASSERT (ok);
+    ASSERT (result == 1.0L);
+    ASSERT (ptr == input + 2);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "-1";
+    const char *ptr;
+    long double result = UNINIT;
+    bool ok = xstrtold (input, &ptr, &result, strtold);
+    ASSERT (ok);
+    ASSERT (result == -1.0L);
+    ASSERT (ptr == input + 2);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "1e0";
+    const char *ptr;
+    long double result = UNINIT;
+    bool ok = xstrtold (input, &ptr, &result, strtold);
+    ASSERT (ok);
+    ASSERT (result == 1.0L);
+    ASSERT (ptr == input + 3);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "1e+0";
+    const char *ptr;
+    long double result = UNINIT;
+    bool ok = xstrtold (input, &ptr, &result, strtold);
+    ASSERT (ok);
+    ASSERT (result == 1.0L);
+    ASSERT (ptr == input + 4);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "1e-0";
+    const char *ptr;
+    long double result = UNINIT;
+    bool ok = xstrtold (input, &ptr, &result, strtold);
+    ASSERT (ok);
+    ASSERT (result == 1.0L);
+    ASSERT (ptr == input + 4);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "1e1";
+    const char *ptr;
+    long double result = UNINIT;
+    bool ok = xstrtold (input, &ptr, &result, strtold);
+    ASSERT (ok);
+    ASSERT (result == 10.0L);
+    ASSERT (ptr == input + 3);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "5e-1";
+    const char *ptr;
+    long double result = UNINIT;
+    bool ok = xstrtold (input, &ptr, &result, strtold);
+    ASSERT (ok);
+    ASSERT (result == 0.5L);
+    ASSERT (ptr == input + 4);
+    ASSERT (errno == 0);
+  }
+
+  /* Zero.  */
+  {
+    const char input[] = "0";
+    const char *ptr;
+    long double result = UNINIT;
+    bool ok = xstrtold (input, &ptr, &result, strtold);
+    ASSERT (ok);
+    ASSERT (result == 0.0L);
+    ASSERT (!signbit (result));
+    ASSERT (ptr == input + 1);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = ".0";
+    const char *ptr;
+    long double result = UNINIT;
+    bool ok = xstrtold (input, &ptr, &result, strtold);
+    ASSERT (ok);
+    ASSERT (result == 0.0L);
+    ASSERT (!signbit (result));
+    ASSERT (ptr == input + 2);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "0e0";
+    const char *ptr;
+    long double result = UNINIT;
+    bool ok = xstrtold (input, &ptr, &result, strtold);
+    ASSERT (ok);
+    ASSERT (result == 0.0L);
+    ASSERT (!signbit (result));
+    ASSERT (ptr == input + 3);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "0e+9999999";
+    const char *ptr;
+    long double result = UNINIT;
+    bool ok = xstrtold (input, &ptr, &result, strtold);
+    ASSERT (ok);
+    ASSERT (result == 0.0L);
+    ASSERT (!signbit (result));
+    ASSERT (ptr == input + 10);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "0e-9999999";
+    const char *ptr;
+    long double result = UNINIT;
+    bool ok = xstrtold (input, &ptr, &result, strtold);
+    ASSERT (ok);
+    ASSERT (result == 0.0L);
+    ASSERT (!signbit (result));
+    ASSERT (ptr == input + 10);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "-0";
+    const char *ptr;
+    long double result = UNINIT;
+    bool ok = xstrtold (input, &ptr, &result, strtold);
+    ASSERT (ok);
+    ASSERT (result == 0.0L);
+    ASSERT (!!signbit (result) == !!signbit (minus_zerol));
+    ASSERT (ptr == input + 2);
+    ASSERT (errno == 0);
+  }
+
+  /* Suffixes.  */
+  {
+    const char input[] = "1f";
+    const char *ptr;
+    long double result = UNINIT;
+    bool ok = xstrtold (input, &ptr, &result, strtold);
+    ASSERT (ok);
+    ASSERT (result == 1.0L);
+    ASSERT (ptr == input + 1);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "1.f";
+    const char *ptr;
+    long double result = UNINIT;
+    bool ok = xstrtold (input, &ptr, &result, strtold);
+    ASSERT (ok);
+    ASSERT (result == 1.0L);
+    ASSERT (ptr == input + 2);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "1e";
+    {
+      const char *ptr;
+      long double result = UNINIT;
+      bool ok = xstrtold (input, &ptr, &result, strtold);
+      ASSERT (ok);
+      ASSERT (result == 1.0L);
+      ASSERT (ptr == input + 1);
+      ASSERT (errno == 0);
+    }
+    {
+      long double result = UNINIT;
+      bool ok = xstrtold (input, NULL, &result, strtold);
+      ASSERT (!ok);
+      ASSERT (result == 1.0L);
+      ASSERT (errno == 0);
+    }
+  }
+  {
+    const char input[] = "1e+";
+    {
+      const char *ptr;
+      long double result = UNINIT;
+      bool ok = xstrtold (input, &ptr, &result, strtold);
+      ASSERT (ok);
+      ASSERT (result == 1.0L);
+      ASSERT (ptr == input + 1);
+      ASSERT (errno == 0);
+    }
+    {
+      long double result = UNINIT;
+      bool ok = xstrtold (input, NULL, &result, strtold);
+      ASSERT (!ok);
+      ASSERT (result == 1.0L);
+      ASSERT (errno == 0);
+    }
+  }
+  {
+    const char input[] = "1e-";
+    {
+      const char *ptr;
+      long double result = UNINIT;
+      bool ok = xstrtold (input, &ptr, &result, strtold);
+      ASSERT (ok);
+      ASSERT (result == 1.0L);
+      ASSERT (ptr == input + 1);
+      ASSERT (errno == 0);
+    }
+    {
+      long double result = UNINIT;
+      bool ok = xstrtold (input, NULL, &result, strtold);
+      ASSERT (!ok);
+      ASSERT (result == 1.0L);
+      ASSERT (errno == 0);
+    }
+  }
+  {
+    const char input[] = "1E 2";
+    {
+      const char *ptr;
+      long double result = UNINIT;
+      bool ok = xstrtold (input, &ptr, &result, strtold);
+      ASSERT (ok);
+      ASSERT (result == 1.0L);
+      ASSERT (ptr == input + 1);
+      ASSERT (errno == 0);
+    }
+    {
+      long double result = UNINIT;
+      bool ok = xstrtold (input, NULL, &result, strtold);
+      ASSERT (!ok);
+      ASSERT (result == 1.0L);
+      ASSERT (errno == 0);
+    }
+  }
+  {
+    const char input[] = "0x";
+    const char *ptr;
+    long double result = UNINIT;
+    bool ok = xstrtold (input, &ptr, &result, strtold);
+    ASSERT (ok);
+    ASSERT (result == 0.0L);
+    ASSERT (!signbit (result));
+    ASSERT (ptr == input + 1);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "00x1";
+    const char *ptr;
+    long double result = UNINIT;
+    bool ok = xstrtold (input, &ptr, &result, strtold);
+    ASSERT (ok);
+    ASSERT (result == 0.0L);
+    ASSERT (!signbit (result));
+    ASSERT (ptr == input + 2);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "-0x";
+    const char *ptr;
+    long double result = UNINIT;
+    bool ok = xstrtold (input, &ptr, &result, strtold);
+    ASSERT (ok);
+    ASSERT (result == 0.0L);
+    ASSERT (!!signbit (result) == !!signbit (minus_zerol));
+    ASSERT (ptr == input + 2);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "0xg";
+    const char *ptr;
+    long double result = UNINIT;
+    bool ok = xstrtold (input, &ptr, &result, strtold);
+    ASSERT (ok);
+    ASSERT (result == 0.0L);
+    ASSERT (!signbit (result));
+    ASSERT (ptr == input + 1);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "0xp";
+    const char *ptr;
+    long double result = UNINIT;
+    bool ok = xstrtold (input, &ptr, &result, strtold);
+    ASSERT (ok);
+    ASSERT (result == 0.0L);
+    ASSERT (!signbit (result));
+    ASSERT (ptr == input + 1);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "0XP";
+    const char *ptr;
+    long double result = UNINIT;
+    bool ok = xstrtold (input, &ptr, &result, strtold);
+    ASSERT (ok);
+    ASSERT (result == 0.0L);
+    ASSERT (!signbit (result));
+    ASSERT (ptr == input + 1);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "0x.";
+    const char *ptr;
+    long double result = UNINIT;
+    bool ok = xstrtold (input, &ptr, &result, strtold);
+    ASSERT (ok);
+    ASSERT (result == 0.0L);
+    ASSERT (!signbit (result));
+    ASSERT (ptr == input + 1);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "0xp+";
+    const char *ptr;
+    long double result = UNINIT;
+    bool ok = xstrtold (input, &ptr, &result, strtold);
+    ASSERT (ok);
+    ASSERT (result == 0.0L);
+    ASSERT (!signbit (result));
+    ASSERT (ptr == input + 1);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "0xp+1";
+    const char *ptr;
+    long double result = UNINIT;
+    bool ok = xstrtold (input, &ptr, &result, strtold);
+    ASSERT (ok);
+    ASSERT (result == 0.0L);
+    ASSERT (!signbit (result));
+    ASSERT (ptr == input + 1);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "0x.p+1";
+    const char *ptr;
+    long double result = UNINIT;
+    bool ok = xstrtold (input, &ptr, &result, strtold);
+    ASSERT (ok);
+    ASSERT (result == 0.0L);
+    ASSERT (!signbit (result));
+    ASSERT (ptr == input + 1);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "1p+1";
+    const char *ptr;
+    long double result = UNINIT;
+    bool ok = xstrtold (input, &ptr, &result, strtold);
+    ASSERT (ok);
+    ASSERT (result == 1.0L);
+    ASSERT (ptr == input + 1);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "1P+1";
+    const char *ptr;
+    long double result = UNINIT;
+    bool ok = xstrtold (input, &ptr, &result, strtold);
+    ASSERT (ok);
+    ASSERT (result == 1.0L);
+    ASSERT (ptr == input + 1);
+    ASSERT (errno == 0);
+  }
+
+  /* Overflow.  */
+  {
+    const char input[] = "1e6000";
+    const char *ptr;
+    long double result = UNINIT;
+    bool ok = xstrtold (input, &ptr, &result, strtold);
+    ASSERT (!ok);
+    ASSERT (result == HUGE_VALL);
+    ASSERT (ptr == input + 6);
+    ASSERT (errno == ERANGE);
+  }
+  {
+    const char input[] = "1E1000000";
+    const char *ptr;
+    long double result = UNINIT;
+    bool ok = xstrtold (input, &ptr, &result, strtold);
+    ASSERT (!ok);
+    ASSERT (result == HUGE_VALL);
+    ASSERT (ptr == input + 9);
+    ASSERT (errno == ERANGE);
+  }
+  {
+    const char input[] = "-1E1000000";
+    const char *ptr;
+    long double result = UNINIT;
+    bool ok = xstrtold (input, &ptr, &result, strtold);
+    ASSERT (!ok);
+    ASSERT (result == -HUGE_VALL);
+    ASSERT (ptr == input + 10);
+    ASSERT (errno == ERANGE);
+  }
+
+  /* Gradual underflow, resulting in a denormalized number.  */
+  {
+#if LDBL_MAX_EXP > 10000
+    const char input[] = "1e-4950";
+#else
+    const char input[] = "1e-320";
+#endif
+    const char *ptr;
+    long double result = UNINIT;
+    bool ok = xstrtold (input, &ptr, &result, strtold);
+#if defined _MSC_VER
+    ASSERT (ok);
+#else
+    ASSERT (!ok);
+#endif
+    ASSERT (0.0L < result && result <= LDBL_MIN);
+    ASSERT (ptr == input + strlen (input));
+#if defined _MSC_VER
+    ASSERT (errno == 0);
+#else
+    ASSERT (errno == ERANGE);
+#endif
+  }
+  {
+#if LDBL_MAX_EXP > 10000
+    const char input[] = "-1e-4950";
+#else
+    const char input[] = "-1e-320";
+#endif
+    const char *ptr;
+    long double result = UNINIT;
+    bool ok = xstrtold (input, &ptr, &result, strtold);
+#if defined _MSC_VER
+    ASSERT (ok);
+#else
+    ASSERT (!ok);
+#endif
+    ASSERT (-LDBL_MIN <= result && result < 0.0L);
+    ASSERT (ptr == input + strlen (input));
+#if defined _MSC_VER
+    ASSERT (errno == 0);
+#else
+    ASSERT (errno == ERANGE);
+#endif
+  }
+
+  /* Flush-to-zero underflow.  */
+  {
+    const char input[] = "1E-100000";
+    const char *ptr;
+    long double result = UNINIT;
+    bool ok = xstrtold (input, &ptr, &result, strtold);
+    ASSERT (ok);
+    ASSERT (result == 0.0L);
+    ASSERT (!signbit (result));
+    ASSERT (ptr == input + 9);
+    ASSERT (errno == ERANGE);
+  }
+  {
+    const char input[] = "-1E-100000";
+    const char *ptr;
+    long double result = UNINIT;
+    bool ok = xstrtold (input, &ptr, &result, strtold);
+    ASSERT (ok);
+    ASSERT (result == 0.0L);
+    /* Negative underflow.  Expect a negative sign, although POSIX allows +0.0L.
+       See also <https://sourceware.org/bugzilla/show_bug.cgi?id=5995>.  */
+    ASSERT (!!signbit (result) == !!signbit (minus_zerol));
+    ASSERT (ptr == input + 10);
+    ASSERT (errno == ERANGE);
+  }
+
+  /* Space before the exponent.  */
+  {
+    const char input[] = "1E 1000000";
+    const char *ptr;
+    long double result = UNINIT;
+    bool ok = xstrtold (input, &ptr, &result, strtold);
+    ASSERT (ok);
+    ASSERT (result == 1.0L);
+    ASSERT (ptr == input + 1);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "0x1P 1000000";
+    const char *ptr;
+    long double result = UNINIT;
+    bool ok = xstrtold (input, &ptr, &result, strtold);
+    ASSERT (ok);
+    ASSERT (result == 1.0L);
+    ASSERT (ptr == input + 3);
+    ASSERT (errno == 0);
+  }
+
+  /* Infinity.  */
+  {
+    const char input[] = "iNf";
+    const char *ptr;
+    long double result = UNINIT;
+    bool ok = xstrtold (input, &ptr, &result, strtold);
+    ASSERT (ok);
+    ASSERT (result == HUGE_VALL);
+    ASSERT (ptr == input + 3);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "-InF";
+    const char *ptr;
+    long double result = UNINIT;
+    bool ok = xstrtold (input, &ptr, &result, strtold);
+    ASSERT (ok);
+    ASSERT (result == -HUGE_VALL);
+    ASSERT (ptr == input + 4);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "infinite";
+    const char *ptr;
+    long double result = UNINIT;
+    bool ok = xstrtold (input, &ptr, &result, strtold);
+    ASSERT (ok);
+    ASSERT (result == HUGE_VALL);
+    ASSERT (ptr == input + 3);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "infinitY";
+    const char *ptr;
+    long double result = UNINIT;
+    bool ok = xstrtold (input, &ptr, &result, strtold);
+    ASSERT (ok);
+    ASSERT (result == HUGE_VALL);
+    ASSERT (ptr == input + 8);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "infinitY.";
+    const char *ptr;
+    long double result = UNINIT;
+    bool ok = xstrtold (input, &ptr, &result, strtold);
+    ASSERT (ok);
+    ASSERT (result == HUGE_VALL);
+    ASSERT (ptr == input + 8);
+    ASSERT (errno == 0);
+  }
+
+  /* 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";
+    const char *ptr1;
+    const char *ptr2;
+    long double result1 = UNINIT;
+    long double result2 = UNINIT;
+    bool ok1 = xstrtold (input, &ptr1, &result1, strtold);
+    bool ok2 = xstrtold (input + 1, &ptr2, &result2, strtold);
+    ASSERT (ok1);
+    ASSERT (ok2);
+#if 1 /* All known CPUs support NaNs.  */
+    ASSERT (isnanl (result1));
+    ASSERT (isnanl (result2));
+# 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);
+    ASSERT (ptr2 == input + 4);
+    ASSERT (errno == 0);
+#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(";
+    const char *ptr1;
+    const char *ptr2;
+    long double result1 = UNINIT;
+    long double result2 = UNINIT;
+    bool ok1 = xstrtold (input, &ptr1, &result1, strtold);
+    bool ok2 = xstrtold (input + 1, &ptr2, &result2, strtold);
+    ASSERT (ok1);
+    ASSERT (ok2);
+#if 1 /* All known CPUs support NaNs.  */
+    ASSERT (isnanl (result1));
+    ASSERT (isnanl (result2));
+    ASSERT (!!signbit (result1) == !!signbit (result2));
+    ASSERT (ptr1 == input + 4);
+    ASSERT (ptr2 == input + 4);
+    ASSERT (errno == 0);
+#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()";
+    const char *ptr1;
+    const char *ptr2;
+    long double result1 = UNINIT;
+    long double result2 = UNINIT;
+    bool ok1 = xstrtold (input, &ptr1, &result1, strtold);
+    bool ok2 = xstrtold (input + 1, &ptr2, &result2, strtold);
+    ASSERT (ok1);
+    ASSERT (ok2);
+#if 1 /* All known CPUs support NaNs.  */
+    ASSERT (isnanl (result1));
+    ASSERT (isnanl (result2));
+# if 0
+    /* Sign bits of NaN is a portability sticking point, not worth
+       worrying about.  */
+    ASSERT (!!signbit (result1) != !!signbit (result2));
+# endif
+    ASSERT (ptr1 == input + 6);
+    ASSERT (ptr2 == input + 6);
+    ASSERT (errno == 0);
+#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().";
+    const char *ptr;
+    long double result = UNINIT;
+    bool ok = xstrtold (input, &ptr, &result, strtold);
+    ASSERT (ok);
+#if 1 /* All known CPUs support NaNs.  */
+    ASSERT (isnanl (result));
+    ASSERT (ptr == input + 6);
+    ASSERT (errno == 0);
+#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).";
+    const char *ptr1;
+    const char *ptr2;
+    long double result1 = UNINIT;
+    long double result2 = UNINIT;
+    bool ok1 = xstrtold (input, &ptr1, &result1, strtold);
+    bool ok2 = xstrtold (input + 1, &ptr2, &result2, strtold);
+    ASSERT (ok1);
+    ASSERT (ok2);
+#if 1 /* All known CPUs support NaNs.  */
+    ASSERT (isnanl (result1));
+    ASSERT (isnanl (result2));
+# 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);
+    ASSERT (ptr2 == input + 7);
+    ASSERT (errno == 0);
+#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";
+    const char *ptr;
+    long double result = UNINIT;
+    bool ok = xstrtold (input, &ptr, &result, strtold);
+    ASSERT (ok);
+    ASSERT (result == 10.0L);
+    ASSERT (ptr == input + 3);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "0XA";
+    const char *ptr;
+    long double result = UNINIT;
+    bool ok = xstrtold (input, &ptr, &result, strtold);
+    ASSERT (ok);
+    ASSERT (result == 10.0L);
+    ASSERT (ptr == input + 3);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "0x1p";
+    const char *ptr;
+    long double result = UNINIT;
+    bool ok = xstrtold (input, &ptr, &result, strtold);
+    ASSERT (ok);
+    ASSERT (result == 1.0L);
+    ASSERT (ptr == input + 3);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "0x1p+";
+    const char *ptr;
+    long double result = UNINIT;
+    bool ok = xstrtold (input, &ptr, &result, strtold);
+    ASSERT (ok);
+    ASSERT (result == 1.0L);
+    ASSERT (ptr == input + 3);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "0x1P+";
+    const char *ptr;
+    long double result = UNINIT;
+    bool ok = xstrtold (input, &ptr, &result, strtold);
+    ASSERT (ok);
+    ASSERT (result == 1.0L);
+    ASSERT (ptr == input + 3);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "0x1p+1";
+    const char *ptr;
+    long double result = UNINIT;
+    bool ok = xstrtold (input, &ptr, &result, strtold);
+    ASSERT (ok);
+    ASSERT (result == 2.0L);
+    ASSERT (ptr == input + 6);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "0X1P+1";
+    const char *ptr;
+    long double result = UNINIT;
+    bool ok = xstrtold (input, &ptr, &result, strtold);
+    ASSERT (ok);
+    ASSERT (result == 2.0L);
+    ASSERT (ptr == input + 6);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "0x1p+1a";
+    const char *ptr;
+    long double result = UNINIT;
+    bool ok = xstrtold (input, &ptr, &result, strtold);
+    ASSERT (ok);
+    ASSERT (result == 2.0L);
+    ASSERT (ptr == input + 6);
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "0x1p 2";
+    const char *ptr;
+    long double result = UNINIT;
+    bool ok = xstrtold (input, &ptr, &result, strtold);
+    ASSERT (ok);
+    ASSERT (result == 1.0L);
+    ASSERT (ptr == input + 3);
+    ASSERT (errno == 0);
+  }
+
+  /* Large buffers.  */
+  {
+    size_t m = 1000000;
+    char *input = malloc (m + 1);
+    if (input)
+      {
+        const char *ptr;
+        long double result = UNINIT;
+        memset (input, '\t', m - 1);
+        input[m - 1] = '1';
+        input[m] = '\0';
+        bool ok = xstrtold (input, &ptr, &result, strtold);
+        ASSERT (ok);
+        ASSERT (result == 1.0L);
+        ASSERT (ptr == input + m);
+        ASSERT (errno == 0);
+      }
+    free (input);
+  }
+  {
+    size_t m = 1000000;
+    char *input = malloc (m + 1);
+    if (input)
+      {
+        const char *ptr;
+        long double result = UNINIT;
+        memset (input, '0', m - 1);
+        input[m - 1] = '1';
+        input[m] = '\0';
+        bool ok = xstrtold (input, &ptr, &result, strtold);
+        ASSERT (ok);
+        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)
+      {
+        const char *ptr;
+        long double result = UNINIT;
+        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';
+        bool ok = xstrtold (input, &ptr, &result, strtold);
+        ASSERT (ok);
+        ASSERT (result == 1.0L);
+        ASSERT (ptr == input + m);
+        ASSERT (errno == 0);
+      }
+    free (input);
+  }
+  {
+    size_t m = 1000000;
+    char *input = malloc (m + 1);
+    if (input)
+      {
+        const char *ptr;
+        long double result = UNINIT;
+        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';
+        bool ok = xstrtold (input, &ptr, &result, strtold);
+        ASSERT (ok);
+        ASSERT (result == 1.0L);
+        ASSERT (ptr == input + m);
+        ASSERT (errno == 0);
+      }
+    free (input);
+  }
+#endif
+  {
+    size_t m = 1000000;
+    char *input = malloc (m + 1);
+    if (input)
+      {
+        const char *ptr;
+        long double result = UNINIT;
+        input[0] = '-';
+        input[1] = '0';
+        input[2] = 'e';
+        input[3] = '1';
+        memset (input + 4, '0', m - 3);
+        input[m] = '\0';
+        bool ok = xstrtold (input, &ptr, &result, strtold);
+        ASSERT (ok);
+        ASSERT (result == 0.0L);
+        ASSERT (!!signbit (result) == !!signbit (minus_zerol));
+        ASSERT (ptr == input + m);
+        ASSERT (errno == 0);
+      }
+    free (input);
+  }
+
+  return test_exit_status;
+}
-- 
2.34.1

From 4f0e77290636a5b8b8310af4f1561d2ca35a32f1 Mon Sep 17 00:00:00 2001
From: Bruno Haible <br...@clisp.org>
Date: Tue, 23 Jul 2024 20:58:45 +0200
Subject: [PATCH 3/3] xstrtod, xstrtold: Add documentation.

* lib/xstrtod.h (xstrtod, xstrtold): Add comments.
* lib/xstrtod.c (XSTRTOD): Improve comments.
---
 ChangeLog     |  6 ++++++
 lib/xstrtod.c |  9 ++++++---
 lib/xstrtod.h | 47 +++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 59 insertions(+), 3 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 890cd57c2f..13e01f2684 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+2024-07-23  Bruno Haible  <br...@clisp.org>
+
+	xstrtod, xstrtold: Add documentation.
+	* lib/xstrtod.h (xstrtod, xstrtold): Add comments.
+	* lib/xstrtod.c (XSTRTOD): Improve comments.
+
 2024-07-23  Bruno Haible  <br...@clisp.org>
 
 	xstrtold: Add tests.
diff --git a/lib/xstrtod.c b/lib/xstrtod.c
index 55b4bb4a8f..a9ca7e8d10 100644
--- a/lib/xstrtod.c
+++ b/lib/xstrtod.c
@@ -56,9 +56,10 @@ XSTRTOD (char const *str, char const **ptr, DOUBLE *result,
     ok = false;
   else
     {
-      /* Allow underflow (in which case CONVERT returns zero),
-         but flag overflow as an error.  The user can decide
-         to use the limits in RESULT upon ERANGE.  */
+      /* Flag overflow as an error.
+         Flag gradual underflow as an error.
+         Flag flush-to-zero underflow as no error.
+         In either case, the caller can inspect *RESULT to get more details.  */
       if (val != 0 && errno == ERANGE)
         ok = false;
     }
@@ -66,6 +67,8 @@ XSTRTOD (char const *str, char const **ptr, DOUBLE *result,
   if (ptr != NULL)
     *ptr = terminator;
 
+  /* Callers rely on *RESULT even when !ok.  */
   *result = val;
+
   return ok;
 }
diff --git a/lib/xstrtod.h b/lib/xstrtod.h
index 9ae4be13da..8ceca6c433 100644
--- a/lib/xstrtod.h
+++ b/lib/xstrtod.h
@@ -26,8 +26,55 @@ extern "C" {
 #endif
 
 
+/* Converts the initial portion of the string starting at STR (if PTR != NULL)
+   or the entire string starting at STR (if PTR == NULL) to a number of type
+   'double'.
+   CONVERT should be a conversion function with the same calling conventions
+   as strtod, such as strtod or c_strtod.
+   If successful, it returns true, with *RESULT set to the result.
+   If it fails, it returns false, with *RESULT and errno set.
+   In total, there are the following cases:
+
+   Condition                                          RET     *RESULT     errno
+   -----------------------------------------------------------------------------
+   conversion error: no number parsed                 false   0.0         EINVAL or 0
+   PTR == NULL, number parsed but junk after number   false   value       0
+   NaN                                                true    NaN         0
+   ±Infinity                                          true    ±HUGE_VAL   0
+   overflow                                           false   ±HUGE_VAL   ERANGE
+   gradual underflow [!MSVC]                          false   near zero   ERANGE
+   gradual underflow [MSVC]                           true    near zero   0
+   flush-to-zero underflow                            true    ±0.0        ERANGE
+   other finite value                                 true    value       0
+
+   In both cases, if PTR != NULL, *PTR is set to point to the character after
+   the parsed number.  */
 bool xstrtod (const char *str, const char **ptr, double *result,
               double (*convert) (char const *, char **));
+
+/* Converts the initial portion of the string starting at STR (if PTR != NULL)
+   or the entire string starting at STR (if PTR == NULL) to a number of type
+   'long double'.
+   CONVERT should be a conversion function with the same calling conventions
+   as strtold, such as strtold or c_strtold.
+   If successful, it returns true, with *RESULT set to the result.
+   If it fails, it returns false, with *RESULT and errno set.
+   In total, there are the following cases:
+
+   Condition                                          RET     *RESULT     errno
+   -----------------------------------------------------------------------------
+   conversion error: no number parsed                 false   0.0L        EINVAL or 0
+   PTR == NULL, number parsed but junk after number   false   value       0
+   NaN                                                true    NaN         0
+   ±Infinity                                          true    ±HUGE_VALL  0
+   overflow                                           false   ±HUGE_VALL  ERANGE
+   gradual underflow [!MSVC]                          false   near zero   ERANGE
+   gradual underflow [MSVC]                           true    near zero   0
+   flush-to-zero underflow                            true    ±0.0L       ERANGE
+   other finite value                                 true    value       0
+
+   In both cases, if PTR != NULL, *PTR is set to point to the character after
+   the parsed number.  */
 bool xstrtold (const char *str, const char **ptr, long double *result,
                long double (*convert) (char const *, char **));
 
-- 
2.34.1

Reply via email to