The behaviour of math functions is "specified" (or, more exactly, left
to the implementation) in ISO C 23 § 7.12.1.(6).

In the case of the strtof, strtod, strtold functions, a test program
(attached: foo.c) showed that these functions show the following behaviour:

  * Errors are generally reported through errno. *Not* through <fenv.h>
    exceptions. Regardless of math_errhandling.

  * Generally (aside of bugs on specific platforms) flush-to-zero underflow
    is reported by errno = ERANGE.

  * Generally (aside of bugs on specific platforms) gradual underflow is
    handled properly (i.e. produces a non-zero result with absolute value
    < FLT_MIN | DBL_MIN | LDBL_MIN), and on all platforms except MSVC it
    is reported by errno = ERANGE.

Gnulib's replacement function, when not using the system function, fails
to set errno upon gradual underflow. This is allowed by ISO C, but still
makes the the replacement function work worse than what glibc does.
Patch 0001 fixes that.

For strtof, the code intended to invoke the system's strtof if possible.
However, this did not work, as the C macro HAVE_STRTOF was never defined.
Patch 0002 fixes that.

The foo.c test shows no clear rule of thumb whether a system's function
sets errno = ERANGE in the two cases. Therefore patch 0003 determines this
at configure time and uses the information in the implementation of the
replacement function.

Patches 0004, 0005, 0006 correct the documentation, improve comments, and
add an overflow handling test (for mingw, which is buggy in that area as
well).

Patch 0007, finally, adds some more overflow tests, because in mingw
strtof, "1e50" and "1e1000000" produce different behaviour(!).


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

        strtof, strtod, strtold tests: Strengthen tests.
        * tests/test-strtof.h (test_function): Add another overflow test.
        * tests/test-strtod.h (test_function): Likewise.
        * tests/test-strtold.h (test_function): Likewise.

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

        strtold: Revisit underflow behaviour.
        * doc/posix-functions/strtold.texi: Mention broken mingw 5.0. Mention
        that gradual underflow does not count as an error on MSVC.
        * tests/test-strtold.h (test_function): Add a gradual underflow test.
        Check the sign in case of flush-to-zero underflow.

        strtod: Revisit underflow behaviour.
        * doc/posix-functions/strtod.texi: Mention the macOS bug. Mention that
        gradual underflow does not count as an error on Cygwin 2.9 and MSVC.
        * m4/strtod.m4 (gl_FUNC_STRTOD): Update comment.
        * tests/test-strtod.h (test_function): Add a gradual underflow test.
        Check the sign in case of flush-to-zero underflow.

        strtof: Revisit underflow behaviour.
        * doc/posix-functions/strtof.texi: Mention the macOS bug. Mention the
        mingw overflow bug. Mention the underflow bugs on Cygwin 2.9 and mingw.
        Mention that gradual underflow does not count as an error on Cygwin 2.9,
        mingw, MSVC.
        * m4/strtof.m4 (gl_FUNC_STRTOF): Test against the mingw overflow bug.
        * tests/test-strtof.h (test_function): Add a gradual underflow test.
        Check the sign in case of flush-to-zero underflow.

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

        strtof, strtod, strtold: Fix underflow behaviour of system function.
        * m4/strtof.m4 (gl_FUNC_STRTOF): Test for strtof's behaviour upon
        underflow. Conditionally define STRTOF_HAS_UNDERFLOW_BUG,
        STRTOF_HAS_GRADUAL_UNDERFLOW_PROBLEM.
        * m4/strtod.m4 (gl_FUNC_STRTOD): Test for strtod's behaviour upon
        underflow. Conditionally define STRTOD_HAS_UNDERFLOW_BUG,
        STRTOD_HAS_GRADUAL_UNDERFLOW_PROBLEM.
        * m4/strtold.m4 (gl_FUNC_STRTOLD): Test for strtold's behaviour upon
        gradual underflow. Conditionally define
        STRTOLD_HAS_GRADUAL_UNDERFLOW_PROBLEM.
        * lib/strtod.c (HAVE_UNDERLYING_STRTOD): Test STRTOF_HAS_UNDERFLOW_BUG,
        STRTOD_HAS_UNDERFLOW_BUG.
        (HAS_GRADUAL_UNDERFLOW_PROBLEM): New macro.
        (SET_ERRNO_UPON_GRADUAL_UNDERFLOW): New macro.
        (STRTOD): Use it.

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

        strtof: Use the system's strtof() if available.
        * m4/strtof.m4 (gl_FUNC_STRTOF): Define HAVE_STRTOF if strtof exists.

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

        strtof, strtod, strtold: Set errno upon gradual underflow.
        * lib/strtod.c (scale_radix_exp): If the result is a denormalized
        number, set errno to ERANGE.

#include <errno.h>
#include <fenv.h>
#include <float.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>

volatile double z;
volatile long double lz;

int main ()
{
  /* strtof */

  {
    errno = 0;
    feclearexcept (FE_ALL_EXCEPT);
    char *end;
    z = strtof ("1e50", &end);
    int err = errno;
    printf ("strtof +overflow     = %d %d %g\n", fetestexcept (FE_OVERFLOW) ? 1 : 0, err == ERANGE, z);
  }

  {
    errno = 0;
    feclearexcept (FE_ALL_EXCEPT);
    char *end;
    z = strtof ("-1e50", &end);
    int err = errno;
    printf ("strtof -overflow     = %d %d %g\n", fetestexcept (FE_OVERFLOW) ? 1 : 0, err == ERANGE, z);
  }

  {
    errno = 0;
    feclearexcept (FE_ALL_EXCEPT);
    char *end;
    z = strtof ("1e-40", &end);
    int err = errno;
    printf ("strtof +underflow    = %d %d %g\n", fetestexcept (FE_UNDERFLOW) ? 1 : 0, err == ERANGE, z);
  }

  {
    errno = 0;
    feclearexcept (FE_ALL_EXCEPT);
    char *end;
    z = strtof ("-1e-40", &end);
    int err = errno;
    printf ("strtof -underflow    = %d %d %g\n", fetestexcept (FE_UNDERFLOW) ? 1 : 0, err == ERANGE, z);
  }

  {
    errno = 0;
    feclearexcept (FE_ALL_EXCEPT);
    char *end;
    z = strtof ("1e-50", &end);
    int err = errno;
    printf ("strtof +underflow_0  = %d %d %g\n", fetestexcept (FE_UNDERFLOW) ? 1 : 0, err == ERANGE, z);
  }

  {
    errno = 0;
    feclearexcept (FE_ALL_EXCEPT);
    char *end;
    z = strtof ("-1e-50", &end);
    int err = errno;
    printf ("strtof -underflow_0  = %d %d %g\n", fetestexcept (FE_UNDERFLOW) ? 1 : 0, err == ERANGE, z);
  }

  /* strtod */

  {
    errno = 0;
    feclearexcept (FE_ALL_EXCEPT);
    char *end;
    z = strtod ("1e500", &end);
    int err = errno;
    printf ("strtod +overflow     = %d %d %g\n", fetestexcept (FE_OVERFLOW) ? 1 : 0, err == ERANGE, z);
  }

  {
    errno = 0;
    feclearexcept (FE_ALL_EXCEPT);
    char *end;
    z = strtod ("-1e500", &end);
    int err = errno;
    printf ("strtod -overflow     = %d %d %g\n", fetestexcept (FE_OVERFLOW) ? 1 : 0, err == ERANGE, z);
  }

  {
    errno = 0;
    feclearexcept (FE_ALL_EXCEPT);
    char *end;
    z = strtod ("1e-320", &end);
    int err = errno;
    printf ("strtod +underflow    = %d %d %g\n", fetestexcept (FE_UNDERFLOW) ? 1 : 0, err == ERANGE, z);
  }

  {
    errno = 0;
    feclearexcept (FE_ALL_EXCEPT);
    char *end;
    z = strtod ("-1e-320", &end);
    int err = errno;
    printf ("strtod -underflow    = %d %d %g\n", fetestexcept (FE_UNDERFLOW) ? 1 : 0, err == ERANGE, z);
  }

  {
    errno = 0;
    feclearexcept (FE_ALL_EXCEPT);
    char *end;
    z = strtod ("1e-400", &end);
    int err = errno;
    printf ("strtod +underflow_0  = %d %d %g\n", fetestexcept (FE_UNDERFLOW) ? 1 : 0, err == ERANGE, z);
  }

  {
    errno = 0;
    feclearexcept (FE_ALL_EXCEPT);
    char *end;
    z = strtod ("-1e-400", &end);
    int err = errno;
    printf ("strtod -underflow_0  = %d %d %g\n", fetestexcept (FE_UNDERFLOW) ? 1 : 0, err == ERANGE, z);
  }

  /* strtold */

  {
    errno = 0;
    feclearexcept (FE_ALL_EXCEPT);
    char *end;
    lz = strtold ("1e6000", &end);
    int err = errno;
    printf ("strtold +overflow    = %d %d %Lg\n", fetestexcept (FE_OVERFLOW) ? 1 : 0, err == ERANGE, lz);
  }

  {
    errno = 0;
    feclearexcept (FE_ALL_EXCEPT);
    char *end;
    lz = strtold ("-1e6000", &end);
    int err = errno;
    printf ("strtold -overflow    = %d %d %Lg\n", fetestexcept (FE_OVERFLOW) ? 1 : 0, err == ERANGE, lz);
  }

  {
    errno = 0;
    feclearexcept (FE_ALL_EXCEPT);
    char *end;
#if LDBL_MAX_EXP > 10000
    lz = strtold ("1e-4950", &end);
#else
    lz = strtold ("1e-320", &end);
#endif
    int err = errno;
    printf ("strtold +underflow   = %d %d %Lg\n", fetestexcept (FE_UNDERFLOW) ? 1 : 0, err == ERANGE, lz);
  }

  {
    errno = 0;
    feclearexcept (FE_ALL_EXCEPT);
    char *end;
#if LDBL_MAX_EXP > 10000
    lz = strtold ("-1e-4950", &end);
#else
    lz = strtold ("-1e-320", &end);
#endif
    int err = errno;
    printf ("strtold -underflow   = %d %d %Lg\n", fetestexcept (FE_UNDERFLOW) ? 1 : 0, err == ERANGE, lz);
  }

  {
    errno = 0;
    feclearexcept (FE_ALL_EXCEPT);
    char *end;
#if LDBL_MAX_EXP > 10000
    lz = strtold ("1e-6000", &end);
#else
    lz = strtold ("1e-400", &end);
#endif
    int err = errno;
    printf ("strtold +underflow_0 = %d %d %Lg\n", fetestexcept (FE_UNDERFLOW) ? 1 : 0, err == ERANGE, lz);
  }

  {
    errno = 0;
    feclearexcept (FE_ALL_EXCEPT);
    char *end;
#if LDBL_MAX_EXP > 10000
    lz = strtold ("-1e-6000", &end);
#else
    lz = strtold ("-1e-400", &end);
#endif
    int err = errno;
    printf ("strtold -underflow_0 = %d %d %Lg\n", fetestexcept (FE_UNDERFLOW) ? 1 : 0, err == ERANGE, lz);
  }

  return 0;
}

/*
 * gcc -Wall foo.c -lm
 */

/*
glibc/x86_64:
strtof +overflow     = 1 1 inf
strtof -overflow     = 1 1 -inf
strtof +underflow    = 1 1 9.99995e-41
strtof -underflow    = 1 1 -9.99995e-41
strtof +underflow_0  = 1 1 0
strtof -underflow_0  = 1 1 -0
strtod +overflow     = 1 1 inf
strtod -overflow     = 1 1 -inf
strtod +underflow    = 1 1 9.99989e-321
strtod -underflow    = 1 1 -9.99989e-321
strtod +underflow_0  = 1 1 0
strtod -underflow_0  = 1 1 -0
strtold +overflow    = 1 1 inf
strtold -overflow    = 1 1 -inf
strtold +underflow   = 1 1 1.09356e-4950
strtold -underflow   = 1 1 -1.09356e-4950
strtold +underflow_0 = 1 1 0
strtold -underflow_0 = 1 1 -0

glibc/alpha, glibc/arm64, glibc/riscv64, glibc/s390x, glibc/sparc64:
strtof +overflow     = 1 1 inf
strtof -overflow     = 1 1 -inf
strtof +underflow    = 1 1 9.99995e-41
strtof -underflow    = 1 1 -9.99995e-41
strtof +underflow_0  = 1 1 0
strtof -underflow_0  = 1 1 -0
strtod +overflow     = 1 1 inf
strtod -overflow     = 1 1 -inf
strtod +underflow    = 1 1 9.99989e-321
strtod -underflow    = 1 1 -9.99989e-321
strtod +underflow_0  = 1 1 0
strtod -underflow_0  = 1 1 -0
strtold +overflow    = 1 1 inf
strtold -overflow    = 1 1 -inf
strtold +underflow   = 1 1 1e-4950
strtold -underflow   = 1 1 -1e-4950
strtold +underflow_0 = 1 1 0
strtold -underflow_0 = 1 1 -0

glibc/m68k:
strtof +overflow     = 0 1 inf
strtof -overflow     = 0 1 -inf
strtof +underflow    = 0 1 9.99995e-41
strtof -underflow    = 0 1 -9.99995e-41
strtof +underflow_0  = 0 1 0
strtof -underflow_0  = 0 1 -0
strtod +overflow     = 0 1 inf
strtod -overflow     = 0 1 -inf
strtod +underflow    = 0 1 9.99989e-321
strtod -underflow    = 0 1 -9.99989e-321
strtod +underflow_0  = 0 1 0
strtod -underflow_0  = 0 1 -0
strtold +overflow    = 0 1 inf
strtold -overflow    = 0 1 -inf
strtold +underflow   = 0 1 9.113e-4951
strtold -underflow   = 0 1 -9.113e-4951
strtold +underflow_0 = 0 1 0
strtold -underflow_0 = 0 1 -0

glibc/mips64:
strtof +overflow     = 1 1 inf
strtof -overflow     = 1 1 -inf
strtof +underflow    = 1 1 9.99995e-41
strtof -underflow    = 1 1 -9.99995e-41
strtof +underflow_0  = 1 1 0
strtof -underflow_0  = 1 1 -0
strtod +overflow     = 1 1 inf
strtod -overflow     = 1 1 -inf
strtod +underflow    = 1 1 9.99989e-321
strtod -underflow    = 1 1 -9.99989e-321
strtod +underflow_0  = 1 1 0
strtod -underflow_0  = 1 1 -0
strtold +overflow    = 1 1 inf
strtold -overflow    = 1 1 -inf
strtold +underflow   = 1 1 9.99989e-321
strtold -underflow   = 1 1 -9.99989e-321
strtold +underflow_0 = 1 1 0
strtold -underflow_0 = 1 1 -0

musl libc/x86_64:
strtof +overflow     = 1 1 inf
strtof -overflow     = 1 1 -inf
strtof +underflow    = 0 1 9.99995e-41
strtof -underflow    = 0 1 -9.99995e-41
strtof +underflow_0  = 1 1 0
strtof -underflow_0  = 1 1 -0
strtod +overflow     = 1 1 inf
strtod -overflow     = 1 1 -inf
strtod +underflow    = 0 1 9.99989e-321
strtod -underflow    = 0 1 -9.99989e-321
strtod +underflow_0  = 1 1 0
strtod -underflow_0  = 1 1 -0
strtold +overflow    = 1 1 inf
strtold -overflow    = 1 1 -inf
strtold +underflow   = 0 1 1.09356e-4950
strtold -underflow   = 0 1 -1.09356e-4950
strtold +underflow_0 = 1 1 0
strtold -underflow_0 = 1 1 -0

macOS 12/arm64, Android/arm:
strtof +overflow     = 0 1 inf
strtof -overflow     = 0 1 -inf
strtof +underflow    = 0 1 9.99995e-41
strtof -underflow    = 0 1 -9.99995e-41
strtof +underflow_0  = 0 1 0
strtof -underflow_0  = 0 1 -0
strtod +overflow     = 0 1 inf
strtod -overflow     = 0 1 -inf
strtod +underflow    = 0 1 9.99989e-321
strtod -underflow    = 0 1 -9.99989e-321
strtod +underflow_0  = 1 1 0
strtod -underflow_0  = 1 1 -0
strtold +overflow    = 0 1 inf
strtold -overflow    = 0 1 -inf
strtold +underflow   = 0 1 9.99989e-321
strtold -underflow   = 0 1 -9.99989e-321
strtold +underflow_0 = 1 1 0
strtold -underflow_0 = 1 1 -0

FreeBSD/x86_64, NetBSD/x86_64, OpenBSD/x86_64:
strtof +overflow     = 0 1 inf
strtof -overflow     = 0 1 -inf
strtof +underflow    = 0 1 9.99995e-41
strtof -underflow    = 0 1 -9.99995e-41
strtof +underflow_0  = 0 1 0
strtof -underflow_0  = 0 1 -0
strtod +overflow     = 0 1 inf
strtod -overflow     = 0 1 -inf
strtod +underflow    = 0 1 9.99989e-321
strtod -underflow    = 0 1 -9.99989e-321
strtod +underflow_0  = 1 1 0
strtod -underflow_0  = 1 1 -0
strtold +overflow    = 0 1 inf
strtold -overflow    = 0 1 -inf
strtold +underflow   = 0 1 1.09356e-4950
strtold -underflow   = 0 1 -1.09356e-4950
strtold +underflow_0 = 0 1 0
strtold -underflow_0 = 0 1 -0

AIX/powerpc64:
strtof +overflow     = 1 1 INF
strtof -overflow     = 1 1 -INF
strtof +underflow    = 1 1 9.99995e-41
strtof -underflow    = 1 1 -9.99995e-41
strtof +underflow_0  = 1 1 0
strtof -underflow_0  = 1 1 -0
strtod +overflow     = 1 1 INF
strtod -overflow     = 1 1 -INF
strtod +underflow    = 1 1 9.99989e-321
strtod -underflow    = 1 1 -9.99989e-321
strtod +underflow_0  = 1 1 0
strtod -underflow_0  = 1 1 -0
strtold +overflow    = 1 1 INF
strtold -overflow    = 1 1 -INF
strtold +underflow   = 1 1 9.99989e-321
strtold -underflow   = 1 1 -9.99989e-321
strtold +underflow_0 = 1 1 0
strtold -underflow_0 = 1 1 -0

Solaris 10/x86_64, Solaris 11/x86_64:
strtof +overflow     = 1 1 Inf
strtof -overflow     = 1 1 -Inf
strtof +underflow    = 1 1 9.99995e-41
strtof -underflow    = 1 1 -9.99995e-41
strtof +underflow_0  = 1 1 0
strtof -underflow_0  = 1 1 -0
strtod +overflow     = 1 1 Inf
strtod -overflow     = 1 1 -Inf
strtod +underflow    = 1 1 9.99989e-321
strtod -underflow    = 1 1 -9.99989e-321
strtod +underflow_0  = 1 1 0
strtod -underflow_0  = 1 1 -0
strtold +overflow    = 1 1 Inf
strtold -overflow    = 1 1 -Inf
strtold +underflow   = 1 1 1.09356e-4950
strtold -underflow   = 1 1 -1.09356e-4950
strtold +underflow_0 = 1 1 0
strtold -underflow_0 = 1 1 -0

Solaris 10/sparc:
strtof +overflow     = 1 1 Inf
strtof -overflow     = 1 1 -Inf
strtof +underflow    = 1 1 9.99995e-41
strtof -underflow    = 1 1 -9.99995e-41
strtof +underflow_0  = 1 1 0
strtof -underflow_0  = 1 1 -0
strtod +overflow     = 1 1 Inf
strtod -overflow     = 1 1 -Inf
strtod +underflow    = 1 1 9.99989e-321
strtod -underflow    = 1 1 -9.99989e-321
strtod +underflow_0  = 1 1 0
strtod -underflow_0  = 1 1 -0
strtold +overflow    = 1 1 Inf
strtold -overflow    = 1 1 -Inf
strtold +underflow   = 1 1 1e-4950
strtold -underflow   = 1 1 -1e-4950
strtold +underflow_0 = 1 1 0
strtold -underflow_0 = 1 1 -0

Cygwin 2.9.0/x86_64:
strtof +overflow     = 1 1 inf
strtof -overflow     = 1 1 -inf
strtof +underflow    = 1 0 9.99995e-41
strtof -underflow    = 1 0 -9.99995e-41
strtof +underflow_0  = 1 0 0
strtof -underflow_0  = 1 0 -0
strtod +overflow     = 0 1 inf
strtod -overflow     = 0 1 -inf
strtod +underflow    = 0 0 9.99989e-321
strtod -underflow    = 0 0 -9.99989e-321
strtod +underflow_0  = 1 1 0
strtod -underflow_0  = 1 1 -0
strtold +overflow    = 0 1 inf
strtold -overflow    = 0 1 -inf
strtold +underflow   = 0 0 1.09356e-4950
strtold -underflow   = 0 0 -1.09356e-4950
strtold +underflow_0 = 0 0 0
strtold -underflow_0 = 0 0 -0

Cygwin 3.4.6/x86_64:
strtof +overflow     = 1 1 inf
strtof -overflow     = 1 1 -inf
strtof +underflow    = 1 1 9.99995e-41
strtof -underflow    = 1 1 -9.99995e-41
strtof +underflow_0  = 1 1 0
strtof -underflow_0  = 1 1 -0
strtod +overflow     = 0 1 inf
strtod -overflow     = 0 1 -inf
strtod +underflow    = 0 1 9.99989e-321
strtod -underflow    = 0 1 -9.99989e-321
strtod +underflow_0  = 1 1 0
strtod -underflow_0  = 1 1 -0
strtold +overflow    = 0 1 inf
strtold -overflow    = 0 1 -inf
strtold +underflow   = 0 1 1.09356e-4950
strtold -underflow   = 0 1 -1.09356e-4950
strtold +underflow_0 = 0 1 0
strtold -underflow_0 = 0 1 -0

mingw 5/x86_64 without __USE_MINGW_ANSI_STDIO:
strtof +overflow     = 1 0 1.#INF
strtof -overflow     = 1 0 -1.#INF
strtof +underflow    = 1 0 9.99995e-041
strtof -underflow    = 1 0 -9.99995e-041
strtof +underflow_0  = 1 0 0
strtof -underflow_0  = 1 0 -0
strtod +overflow     = 0 1 1.#INF
strtod -overflow     = 0 1 -1.#INF
strtod +underflow    = 0 1 9.99989e-321
strtod -underflow    = 0 1 -9.99989e-321
strtod +underflow_0  = 0 1 0
strtod -underflow_0  = 0 1 -0
strtold +overflow    = 0 1 3.43181r-317
strtold -overflow    = 0 1 3.43181r-317
strtold +underflow   = 0 0 3.43181r-317
strtold -underflow   = 0 0 3.43181r-317
strtold +underflow_0 = 0 1 3.43181r-317
strtold -underflow_0 = 0 1 3.43181r-317

mingw 5/x86_64 with __USE_MINGW_ANSI_STDIO, mingw 10/x86_64:
strtof +overflow     = 0 1 inf
strtof -overflow     = 0 1 -inf
strtof +underflow    = 0 1 9.99995e-41
strtof -underflow    = 0 1 -9.99995e-41
strtof +underflow_0  = 0 1 0
strtof -underflow_0  = 0 1 -0
strtod +overflow     = 0 1 inf
strtod -overflow     = 0 1 -inf
strtod +underflow    = 0 1 9.99989e-321
strtod -underflow    = 0 1 -9.99989e-321
strtod +underflow_0  = 0 1 0
strtod -underflow_0  = 0 1 -0
strtold +overflow    = 0 1 inf
strtold -overflow    = 0 1 -inf
strtold +underflow   = 0 1 1.09356e-4950
strtold -underflow   = 0 1 -1.09356e-4950
strtold +underflow_0 = 0 1 0
strtold -underflow_0 = 0 1 -0

MSVC/x86_64:
strtof +overflow     = 0 1 inf
strtof -overflow     = 0 1 -inf
strtof +underflow    = 0 0 9.99995e-41
strtof -underflow    = 0 0 -9.99995e-41
strtof +underflow_0  = 0 1 0
strtof -underflow_0  = 0 1 -0
strtod +overflow     = 0 1 inf
strtod -overflow     = 0 1 -inf
strtod +underflow    = 0 0 9.99989e-321
strtod -underflow    = 0 0 -9.99989e-321
strtod +underflow_0  = 0 1 0
strtod -underflow_0  = 0 1 -0
strtold +overflow    = 0 1 inf
strtold -overflow    = 0 1 -inf
strtold +underflow   = 0 0 9.99989e-321
strtold -underflow   = 0 0 -9.99989e-321
strtold +underflow_0 = 0 1 0
strtold -underflow_0 = 0 1 -0

*/
>From 6be595445812bbcdf5d11c3f439a3eb3dbd7069a Mon Sep 17 00:00:00 2001
From: Bruno Haible <br...@clisp.org>
Date: Tue, 23 Jul 2024 10:39:28 +0200
Subject: [PATCH 1/7] strtof, strtod, strtold: Set errno upon gradual
 underflow.

* lib/strtod.c (scale_radix_exp): If the result is a denormalized
number, set errno to ERANGE.
---
 ChangeLog    |  6 ++++++
 lib/strtod.c | 13 +++++++++++--
 2 files changed, 17 insertions(+), 2 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index cf20d60b76..90fdb4dee0 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+2024-07-23  Bruno Haible  <br...@clisp.org>
+
+	strtof, strtod, strtold: Set errno upon gradual underflow.
+	* lib/strtod.c (scale_radix_exp): If the result is a denormalized
+	number, set errno to ERANGE.
+
 2024-07-22  Collin Funk  <collin.fu...@gmail.com>
 
 	sys_socket tests: Improve tests for macro definitions.
diff --git a/lib/strtod.c b/lib/strtod.c
index e218a46f71..8847cae940 100644
--- a/lib/strtod.c
+++ b/lib/strtod.c
@@ -158,11 +158,20 @@ scale_radix_exp (DOUBLE x, int radix, long int exponent)
         {
           if (e < 0)
             {
-              while (e++ != 0)
+              for (;;)
                 {
+                  if (e++ == 0)
+                    {
+                      if (r < MIN && r > -MIN)
+                        /* Gradual underflow, resulting in a denormalized
+                           number.  */
+                        errno = ERANGE;
+                      break;
+                    }
                   r /= radix;
-                  if (r == 0 && x != 0)
+                  if (r == 0)
                     {
+                      /* Flush-to-zero underflow.  */
                       errno = ERANGE;
                       break;
                     }
-- 
2.34.1

>From ae0879dafc29a1be482c5db96ddf0e0a1791eb9a Mon Sep 17 00:00:00 2001
From: Bruno Haible <br...@clisp.org>
Date: Tue, 23 Jul 2024 12:40:15 +0200
Subject: [PATCH 2/7] strtof: Use the system's strtof() if available.

* m4/strtof.m4 (gl_FUNC_STRTOF): Define HAVE_STRTOF if strtof exists.
---
 ChangeLog    | 5 +++++
 m4/strtof.m4 | 4 +++-
 2 files changed, 8 insertions(+), 1 deletion(-)

diff --git a/ChangeLog b/ChangeLog
index 90fdb4dee0..8c84fd22d0 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+2024-07-23  Bruno Haible  <br...@clisp.org>
+
+	strtof: Use the system's strtof() if available.
+	* m4/strtof.m4 (gl_FUNC_STRTOF): Define HAVE_STRTOF if strtof exists.
+
 2024-07-23  Bruno Haible  <br...@clisp.org>
 
 	strtof, strtod, strtold: Set errno upon gradual underflow.
diff --git a/m4/strtof.m4 b/m4/strtof.m4
index bdd3df8f48..72c30b3d31 100644
--- a/m4/strtof.m4
+++ b/m4/strtof.m4
@@ -1,5 +1,5 @@
 # strtof.m4
-# serial 1
+# serial 2
 dnl Copyright (C) 2002-2003, 2006-2024 Free Software Foundation, Inc.
 dnl This file is free software; the Free Software Foundation
 dnl gives unlimited permission to copy and/or distribute it,
@@ -19,6 +19,8 @@ AC_DEFUN([gl_FUNC_STRTOF]
     HAVE_STRTOF=0
   fi
   if test $HAVE_STRTOF = 1; then
+    AC_DEFINE([HAVE_STRTOF], [1],
+      [Define to 1 if you have the 'strtof' function.])
     AC_CACHE_CHECK([whether strtof obeys C99], [gl_cv_func_strtof_works],
       [AC_RUN_IFELSE([AC_LANG_PROGRAM([[
 #include <stdlib.h>
-- 
2.34.1

>From cb76ba0b05d382e93957b4724fe0a4c20f583d12 Mon Sep 17 00:00:00 2001
From: Bruno Haible <br...@clisp.org>
Date: Tue, 23 Jul 2024 12:40:19 +0200
Subject: [PATCH 3/7] strtof, strtod, strtold: Fix underflow behaviour of
 system function.

* m4/strtof.m4 (gl_FUNC_STRTOF): Test for strtof's behaviour upon
underflow. Conditionally define STRTOF_HAS_UNDERFLOW_BUG,
STRTOF_HAS_GRADUAL_UNDERFLOW_PROBLEM.
* m4/strtod.m4 (gl_FUNC_STRTOD): Test for strtod's behaviour upon
underflow. Conditionally define STRTOD_HAS_UNDERFLOW_BUG,
STRTOD_HAS_GRADUAL_UNDERFLOW_PROBLEM.
* m4/strtold.m4 (gl_FUNC_STRTOLD): Test for strtold's behaviour upon
gradual underflow. Conditionally define
STRTOLD_HAS_GRADUAL_UNDERFLOW_PROBLEM.
* lib/strtod.c (HAVE_UNDERLYING_STRTOD): Test STRTOF_HAS_UNDERFLOW_BUG,
STRTOD_HAS_UNDERFLOW_BUG.
(HAS_GRADUAL_UNDERFLOW_PROBLEM): New macro.
(SET_ERRNO_UPON_GRADUAL_UNDERFLOW): New macro.
(STRTOD): Use it.
---
 ChangeLog     | 18 ++++++++++++++
 lib/strtod.c  | 34 +++++++++++++++++++++++---
 m4/strtod.m4  | 62 ++++++++++++++++++++++++++++++++++++++++------
 m4/strtof.m4  | 68 ++++++++++++++++++++++++++++++++++++++++++++-------
 m4/strtold.m4 | 44 +++++++++++++++++++++++++++++----
 5 files changed, 202 insertions(+), 24 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 8c84fd22d0..f1e93da002 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,21 @@
+2024-07-23  Bruno Haible  <br...@clisp.org>
+
+	strtof, strtod, strtold: Fix underflow behaviour of system function.
+	* m4/strtof.m4 (gl_FUNC_STRTOF): Test for strtof's behaviour upon
+	underflow. Conditionally define STRTOF_HAS_UNDERFLOW_BUG,
+	STRTOF_HAS_GRADUAL_UNDERFLOW_PROBLEM.
+	* m4/strtod.m4 (gl_FUNC_STRTOD): Test for strtod's behaviour upon
+	underflow. Conditionally define STRTOD_HAS_UNDERFLOW_BUG,
+	STRTOD_HAS_GRADUAL_UNDERFLOW_PROBLEM.
+	* m4/strtold.m4 (gl_FUNC_STRTOLD): Test for strtold's behaviour upon
+	gradual underflow. Conditionally define
+	STRTOLD_HAS_GRADUAL_UNDERFLOW_PROBLEM.
+	* lib/strtod.c (HAVE_UNDERLYING_STRTOD): Test STRTOF_HAS_UNDERFLOW_BUG,
+	STRTOD_HAS_UNDERFLOW_BUG.
+	(HAS_GRADUAL_UNDERFLOW_PROBLEM): New macro.
+	(SET_ERRNO_UPON_GRADUAL_UNDERFLOW): New macro.
+	(STRTOD): Use it.
+
 2024-07-23  Bruno Haible  <br...@clisp.org>
 
 	strtof: Use the system's strtof() if available.
diff --git a/lib/strtod.c b/lib/strtod.c
index 8847cae940..bece59fd68 100644
--- a/lib/strtod.c
+++ b/lib/strtod.c
@@ -40,7 +40,13 @@
 #if defined USE_FLOAT
 # define STRTOD strtof
 # define LDEXP ldexpf
-# define HAVE_UNDERLYING_STRTOD HAVE_STRTOF
+# if STRTOF_HAS_UNDERFLOW_BUG
+   /* strtof would not set errno=ERANGE upon flush-to-zero underflow.  */
+#  define HAVE_UNDERLYING_STRTOD 0
+# else
+#  define HAVE_UNDERLYING_STRTOD HAVE_STRTOF
+# endif
+# define HAS_GRADUAL_UNDERFLOW_PROBLEM STRTOF_HAS_GRADUAL_UNDERFLOW_PROBLEM
 # define DOUBLE float
 # define MIN FLT_MIN
 # define MAX FLT_MAX
@@ -58,7 +64,7 @@
       not a 'long double'.  */
 #  define HAVE_UNDERLYING_STRTOD 0
 # elif STRTOLD_HAS_UNDERFLOW_BUG
-   /* strtold would not set errno=ERANGE upon underflow.  */
+   /* strtold would not set errno=ERANGE upon flush-to-zero underflow.  */
 #  define HAVE_UNDERLYING_STRTOD 0
 # elif defined __MINGW32__ && __MINGW64_VERSION_MAJOR < 10
    /* strtold is broken in mingw versions before 10.0:
@@ -70,6 +76,7 @@
 # else
 #  define HAVE_UNDERLYING_STRTOD HAVE_STRTOLD
 # endif
+# define HAS_GRADUAL_UNDERFLOW_PROBLEM STRTOLD_HAS_GRADUAL_UNDERFLOW_PROBLEM
 # define DOUBLE long double
 # define MIN LDBL_MIN
 # define MAX LDBL_MAX
@@ -82,7 +89,13 @@
 #else
 # define STRTOD strtod
 # define LDEXP ldexp
-# define HAVE_UNDERLYING_STRTOD 1
+# if STRTOD_HAS_UNDERFLOW_BUG
+   /* strtod would not set errno=ERANGE upon flush-to-zero underflow.  */
+#  define HAVE_UNDERLYING_STRTOD 0
+# else
+#  define HAVE_UNDERLYING_STRTOD 1
+# endif
+# define HAS_GRADUAL_UNDERFLOW_PROBLEM STRTOD_HAS_GRADUAL_UNDERFLOW_PROBLEM
 # define DOUBLE double
 # define MIN DBL_MIN
 # define MAX DBL_MAX
@@ -351,10 +364,22 @@ STRTOD (const char *nptr, char **endptr)
 # else
 #  undef strtod
 # endif
+# if HAS_GRADUAL_UNDERFLOW_PROBLEM
+#  define SET_ERRNO_UPON_GRADUAL_UNDERFLOW(RESULT) \
+    do                                                          \
+      {                                                         \
+        if ((RESULT) != 0 && (RESULT) < MIN && (RESULT) > -MIN) \
+          errno = ERANGE;                                       \
+      }                                                         \
+    while (0)
+# else
+#  define SET_ERRNO_UPON_GRADUAL_UNDERFLOW(RESULT) (void)0
+# endif
 #else
 # undef STRTOD
 # define STRTOD(NPTR,ENDPTR) \
    parse_number (NPTR, 10, 10, 1, radixchar, 'e', ENDPTR)
+# define SET_ERRNO_UPON_GRADUAL_UNDERFLOW(RESULT) (void)0
 #endif
 /* From here on, STRTOD refers to the underlying implementation.  It needs
    to handle only finite unsigned decimal numbers with non-null ENDPTR.  */
@@ -382,6 +407,7 @@ STRTOD (const char *nptr, char **endptr)
     ++s;
 
   num = STRTOD (s, &endbuf);
+  SET_ERRNO_UPON_GRADUAL_UNDERFLOW (num);
   end = endbuf;
 
   if (c_isdigit (s[*s == radixchar]))
@@ -424,6 +450,7 @@ STRTOD (const char *nptr, char **endptr)
                     {
                       dup[p - s] = '\0';
                       num = STRTOD (dup, &endbuf);
+                      SET_ERRNO_UPON_GRADUAL_UNDERFLOW (num);
                       saved_errno = errno;
                       free (dup);
                       errno = saved_errno;
@@ -454,6 +481,7 @@ STRTOD (const char *nptr, char **endptr)
                 {
                   dup[e - s] = '\0';
                   num = STRTOD (dup, &endbuf);
+                  SET_ERRNO_UPON_GRADUAL_UNDERFLOW (num);
                   saved_errno = errno;
                   free (dup);
                   errno = saved_errno;
diff --git a/m4/strtod.m4 b/m4/strtod.m4
index 3f8bacc01a..2ccbf3139a 100644
--- a/m4/strtod.m4
+++ b/m4/strtod.m4
@@ -1,5 +1,5 @@
 # strtod.m4
-# serial 29
+# serial 30
 dnl Copyright (C) 2002-2003, 2006-2024 Free Software Foundation, Inc.
 dnl This file is free software; the Free Software Foundation
 dnl gives unlimited permission to copy and/or distribute it,
@@ -24,6 +24,7 @@ AC_DEFUN([gl_FUNC_STRTOD]
     AC_CACHE_CHECK([whether strtod obeys C99], [gl_cv_func_strtod_works],
       [AC_RUN_IFELSE([AC_LANG_PROGRAM([[
 #include <stdlib.h>
+#include <float.h>
 #include <math.h>
 #include <errno.h>
 /* Compare two numbers with ==.
@@ -52,7 +53,7 @@ AC_DEFUN([gl_FUNC_STRTOD]
     char *term;
     strtod (string, &term);
     if (term != string && *(term - 1) == 0)
-      result |= 2;
+      result |= 1;
   }
   {
     /* Older glibc and Cygwin mis-parse "-0x".  */
@@ -61,7 +62,7 @@ AC_DEFUN([gl_FUNC_STRTOD]
     double value = strtod (string, &term);
     double zero = 0.0;
     if (1.0 / value != -1.0 / zero || term != (string + 2))
-      result |= 4;
+      result |= 2;
   }
   {
     /* Many platforms do not parse hex floats.  */
@@ -69,7 +70,7 @@ AC_DEFUN([gl_FUNC_STRTOD]
     char *term;
     double value = strtod (string, &term);
     if (value != 20.0 || term != (string + 6))
-      result |= 8;
+      result |= 4;
   }
   {
     /* Many platforms do not parse infinities.  HP-UX 11.31 parses inf,
@@ -80,7 +81,7 @@ AC_DEFUN([gl_FUNC_STRTOD]
     errno = 0;
     value = strtod (string, &term);
     if (value != HUGE_VAL || term != (string + 3) || errno)
-      result |= 16;
+      result |= 8;
   }
   {
     /* glibc 2.7 and cygwin 1.5.24 misparse "nan()".  */
@@ -88,7 +89,7 @@ AC_DEFUN([gl_FUNC_STRTOD]
     char *term;
     double value = strtod (string, &term);
     if (numeric_equal (value, value) || term != (string + 5))
-      result |= 32;
+      result |= 16;
   }
   {
     /* darwin 10.6.1 misparses "nan(".  */
@@ -96,12 +97,47 @@ AC_DEFUN([gl_FUNC_STRTOD]
     char *term;
     double value = strtod (string, &term);
     if (numeric_equal (value, value) || term != (string + 3))
+      result |= 16;
+  }
+#ifndef _MSC_VER /* On MSVC, this is expected behaviour.  */
+  {
+    /* In Cygwin 2.9, strtod does not set errno upon
+       gradual underflow.  */
+    const char *string = "1e-320";
+    char *term;
+    double value;
+    errno = 0;
+    value = strtod (string, &term);
+    if (term != (string + 6)
+        || (value > 0.0 && value <= DBL_MIN && errno != ERANGE))
+      result |= 32;
+  }
+#endif
+  {
+    /* strtod could not set errno upon
+       flush-to-zero underflow.  */
+    const char *string = "1E-100000";
+    char *term;
+    double value;
+    errno = 0;
+    value = strtod (string, &term);
+    if (term != (string + 9) || (value == 0.0L && errno != ERANGE))
       result |= 64;
   }
   return result;
 ]])],
         [gl_cv_func_strtod_works=yes],
-        [gl_cv_func_strtod_works=no],
+        [result=$?
+         if expr $result '>=' 64 >/dev/null; then
+           gl_cv_func_strtod_works="no (underflow problem)"
+         else
+           if expr $result '>=' 32 >/dev/null; then
+             gl_cv_func_strtod_works="no (gradual underflow problem)"
+           else
+             gl_cv_func_strtod_works=no
+           fi
+         fi
+        ],
         [dnl The last known bugs in glibc strtod(), as of this writing,
          dnl were fixed in version 2.8
          AC_EGREP_CPP([Lucky user],
@@ -118,6 +154,8 @@ AC_DEFUN([gl_FUNC_STRTOD]
            [case "$host_os" in
                                   # Guess yes on musl systems.
               *-musl* | midipix*) gl_cv_func_strtod_works="guessing yes" ;;
+                                  # Guess 'no (gradual underflow problem)' on Cygwin.
+              cygwin*)            gl_cv_func_strtod_works="guessing no (gradual underflow problem)" ;;
                                   # Guess yes on native Windows.
               mingw* | windows*)  gl_cv_func_strtod_works="guessing yes" ;;
               *)                  gl_cv_func_strtod_works="$gl_cross_guess_normal" ;;
@@ -129,6 +167,16 @@ AC_DEFUN([gl_FUNC_STRTOD]
       *yes) ;;
       *)
         REPLACE_STRTOD=1
+        case "$gl_cv_func_strtod_works" in
+          *"no (underflow problem)")
+            AC_DEFINE([STRTOD_HAS_UNDERFLOW_BUG], [1],
+              [Define to 1 if strtod does not set errno upon flush-to-zero underflow.])
+            ;;
+          *"no (gradual underflow problem)")
+            AC_DEFINE([STRTOD_HAS_GRADUAL_UNDERFLOW_PROBLEM], [1],
+              [Define to 1 if strtod does not set errno upon gradual underflow.])
+            ;;
+        esac
         ;;
     esac
   fi
diff --git a/m4/strtof.m4 b/m4/strtof.m4
index 72c30b3d31..e37310ebde 100644
--- a/m4/strtof.m4
+++ b/m4/strtof.m4
@@ -1,5 +1,5 @@
 # strtof.m4
-# serial 2
+# serial 3
 dnl Copyright (C) 2002-2003, 2006-2024 Free Software Foundation, Inc.
 dnl This file is free software; the Free Software Foundation
 dnl gives unlimited permission to copy and/or distribute it,
@@ -24,6 +24,7 @@ AC_DEFUN([gl_FUNC_STRTOF]
     AC_CACHE_CHECK([whether strtof obeys C99], [gl_cv_func_strtof_works],
       [AC_RUN_IFELSE([AC_LANG_PROGRAM([[
 #include <stdlib.h>
+#include <float.h>
 #include <math.h>
 #include <errno.h>
 /* Compare two numbers with ==.
@@ -52,7 +53,7 @@ AC_DEFUN([gl_FUNC_STRTOF]
     char *term;
     strtof (string, &term);
     if (term != string && *(term - 1) == 0)
-      result |= 2;
+      result |= 1;
   }
   {
     /* Older glibc and Cygwin mis-parse "-0x".  */
@@ -61,7 +62,7 @@ AC_DEFUN([gl_FUNC_STRTOF]
     float value = strtof (string, &term);
     float zero = 0.0f;
     if (1.0f / value != -1.0f / zero || term != (string + 2))
-      result |= 4;
+      result |= 2;
   }
   {
     /* Many platforms do not parse hex floats.  */
@@ -69,7 +70,7 @@ AC_DEFUN([gl_FUNC_STRTOF]
     char *term;
     float value = strtof (string, &term);
     if (value != 20.0f || term != (string + 6))
-      result |= 8;
+      result |= 4;
   }
   {
     /* Many platforms do not parse infinities.  HP-UX 11.31 parses inf,
@@ -80,7 +81,7 @@ AC_DEFUN([gl_FUNC_STRTOF]
     errno = 0;
     value = strtof (string, &term);
     if (value != HUGE_VAL || term != (string + 3) || errno)
-      result |= 16;
+      result |= 8;
   }
   {
     /* glibc 2.7 and cygwin 1.5.24 misparse "nan()".  */
@@ -88,7 +89,7 @@ AC_DEFUN([gl_FUNC_STRTOF]
     char *term;
     float value = strtof (string, &term);
     if (numeric_equal (value, value) || term != (string + 5))
-      result |= 32;
+      result |= 16;
   }
   {
     /* darwin 10.6.1 misparses "nan(".  */
@@ -96,12 +97,47 @@ AC_DEFUN([gl_FUNC_STRTOF]
     char *term;
     float value = strtof (string, &term);
     if (numeric_equal (value, value) || term != (string + 3))
+      result |= 16;
+  }
+#ifndef _MSC_VER /* On MSVC, this is expected behaviour.  */
+  {
+    /* In Cygwin 2.9 and mingw 5.0, strtof does not set errno upon
+       gradual underflow.  */
+    const char *string = "1e-40";
+    char *term;
+    float value;
+    errno = 0;
+    value = strtof (string, &term);
+    if (term != (string + 5)
+        || (value > 0.0f && value <= FLT_MIN && errno != ERANGE))
+      result |= 32;
+  }
+#endif
+  {
+    /* In Cygwin 2.9 and mingw 5.0, strtof does not set errno upon
+       flush-to-zero underflow.  */
+    const char *string = "1E-100000";
+    char *term;
+    float value;
+    errno = 0;
+    value = strtof (string, &term);
+    if (term != (string + 9) || (value == 0.0L && errno != ERANGE))
       result |= 64;
   }
   return result;
 ]])],
         [gl_cv_func_strtof_works=yes],
-        [gl_cv_func_strtof_works=no],
+        [result=$?
+         if expr $result '>=' 64 >/dev/null; then
+           gl_cv_func_strtof_works="no (underflow problem)"
+         else
+           if expr $result '>=' 32 >/dev/null; then
+             gl_cv_func_strtof_works="no (gradual underflow problem)"
+           else
+             gl_cv_func_strtof_works=no
+           fi
+         fi
+        ],
         [dnl The last known bugs in glibc strtof(), as of this writing,
          dnl were fixed in version 2.8
          AC_EGREP_CPP([Lucky user],
@@ -118,8 +154,12 @@ AC_DEFUN([gl_FUNC_STRTOF]
            [case "$host_os" in
                                   # Guess yes on musl systems.
               *-musl* | midipix*) gl_cv_func_strtof_works="guessing yes" ;;
-                                  # Guess yes on native Windows.
-              mingw* | windows*)  gl_cv_func_strtof_works="guessing yes" ;;
+                                  # Guess 'no (underflow problem)' on Cygwin.
+              cygwin*)            gl_cv_func_strtof_works="guessing no (underflow problem)" ;;
+                                  # Guess 'no (underflow problem)' on mingw.
+                                  # (Although the results may vary depending on
+                                  # __USE_MINGW_ANSI_STDIO and __USE_MINGW_STRTOX.)
+              mingw* | windows*)  gl_cv_func_strtof_works="guessing no (underflow problem)" ;;
               *)                  gl_cv_func_strtof_works="$gl_cross_guess_normal" ;;
             esac
            ])
@@ -129,6 +169,16 @@ AC_DEFUN([gl_FUNC_STRTOF]
       *yes) ;;
       *)
         REPLACE_STRTOF=1
+        case "$gl_cv_func_strtof_works" in
+          *"no (underflow problem)")
+            AC_DEFINE([STRTOF_HAS_UNDERFLOW_BUG], [1],
+              [Define to 1 if strtof does not set errno upon flush-to-zero underflow.])
+            ;;
+          *"no (gradual underflow problem)")
+            AC_DEFINE([STRTOF_HAS_GRADUAL_UNDERFLOW_PROBLEM], [1],
+              [Define to 1 if strtof does not set errno upon gradual underflow.])
+            ;;
+        esac
         ;;
     esac
   fi
diff --git a/m4/strtold.m4 b/m4/strtold.m4
index 4439b60266..1d46bcd822 100644
--- a/m4/strtold.m4
+++ b/m4/strtold.m4
@@ -1,5 +1,5 @@
 # strtold.m4
-# serial 9
+# serial 10
 dnl Copyright (C) 2002-2003, 2006-2024 Free Software Foundation, Inc.
 dnl This file is free software; the Free Software Foundation
 dnl gives unlimited permission to copy and/or distribute it,
@@ -17,8 +17,10 @@ AC_DEFUN([gl_FUNC_STRTOLD]
     AC_CACHE_CHECK([whether strtold obeys POSIX], [gl_cv_func_strtold_works],
       [AC_RUN_IFELSE([AC_LANG_PROGRAM([[
 #include <stdlib.h>
+#include <float.h>
 #include <math.h>
 #include <errno.h>
+#include <string.h>
 /* Compare two numbers with ==.
    This is a separate function because IRIX 6.5 "cc -O" miscompiles an
    'x == x' test.  */
@@ -80,10 +82,29 @@ AC_DEFUN([gl_FUNC_STRTOLD]
     char *term;
     long double value = strtold (string, &term);
     if (numeric_equal (value, value) || term != (string + 3))
+      result |= 16;
+  }
+#ifndef _MSC_VER /* On MSVC, this is expected behaviour.  */
+  {
+    /* In Cygwin 2.9 and mingw 5.0, strtold does not set errno upon
+       gradual underflow.  */
+# if LDBL_MAX_EXP > 10000
+    const char *string = "1e-4950";
+# else
+    const char *string = "1e-320";
+# endif
+    char *term;
+    long double value;
+    errno = 0;
+    value = strtold (string, &term);
+    if (term != (string + strlen (string))
+        || (value > 0.0L && value <= LDBL_MIN && errno != ERANGE))
       result |= 32;
   }
+#endif
   {
-    /* In Cygwin 2.9, strtold does not set errno upon underflow.  */
+    /* In Cygwin 2.9, strtold does not set errno upon
+       flush-to-zero underflow.  */
     const char *string = "1E-100000";
     char *term;
     long double value;
@@ -95,10 +116,15 @@ AC_DEFUN([gl_FUNC_STRTOLD]
   return result;
 ]])],
         [gl_cv_func_strtold_works=yes],
-        [if expr $? '>=' 64 >/dev/null; then
+        [result=$?
+         if expr $result '>=' 64 >/dev/null; then
            gl_cv_func_strtold_works="no (underflow problem)"
          else
-           gl_cv_func_strtold_works=no
+           if expr $result '>=' 32 >/dev/null; then
+             gl_cv_func_strtold_works="no (gradual underflow problem)"
+           else
+             gl_cv_func_strtold_works=no
+           fi
          fi
         ],
         [dnl The last known bugs in glibc strtold(), as of this writing,
@@ -119,6 +145,10 @@ AC_DEFUN([gl_FUNC_STRTOLD]
               *-musl* | midipix*) gl_cv_func_strtold_works="guessing yes" ;;
                                   # Guess 'no (underflow problem)' on Cygwin.
               cygwin*)            gl_cv_func_strtold_works="guessing no (underflow problem)" ;;
+                                  # Guess 'no (gradual underflow problem)' on mingw.
+                                  # (Although the results may vary depending on
+                                  # __USE_MINGW_ANSI_STDIO.)
+              mingw* | windows*)  gl_cv_func_strtold_works="guessing no (gradual underflow problem)" ;;
               *)                  gl_cv_func_strtold_works="$gl_cross_guess_normal" ;;
             esac
            ])
@@ -131,7 +161,11 @@ AC_DEFUN([gl_FUNC_STRTOLD]
         case "$gl_cv_func_strtold_works" in
           *"no (underflow problem)")
             AC_DEFINE([STRTOLD_HAS_UNDERFLOW_BUG], [1],
-              [Define to 1 if strtold does not set errno upon underflow.])
+              [Define to 1 if strtold does not set errno upon flush-to-zero underflow.])
+            ;;
+          *"no (gradual underflow problem)")
+            AC_DEFINE([STRTOLD_HAS_GRADUAL_UNDERFLOW_PROBLEM], [1],
+              [Define to 1 if strtold does not set errno upon gradual underflow.])
             ;;
         esac
         ;;
-- 
2.34.1

From edf38838fe0e409455ad2fe03bedaf06571d95bb Mon Sep 17 00:00:00 2001
From: Bruno Haible <br...@clisp.org>
Date: Tue, 23 Jul 2024 12:40:33 +0200
Subject: [PATCH 4/7] strtof: Revisit underflow behaviour.

* doc/posix-functions/strtof.texi: Mention the macOS bug. Mention the
mingw overflow bug. Mention the underflow bugs on Cygwin 2.9 and mingw.
Mention that gradual underflow does not count as an error on Cygwin 2.9,
mingw, MSVC.
* m4/strtof.m4 (gl_FUNC_STRTOF): Test against the mingw overflow bug.
* tests/test-strtof.h (test_function): Add a gradual underflow test.
Check the sign in case of flush-to-zero underflow.
---
 ChangeLog                       | 11 +++++++++
 doc/posix-functions/strtof.texi | 22 +++++++++++++++++-
 m4/strtof.m4                    | 15 +++++++++++--
 tests/test-strtof.h             | 40 +++++++++++++++++++++++++++------
 4 files changed, 78 insertions(+), 10 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index f1e93da002..3da7b01049 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,14 @@
+2024-07-23  Bruno Haible  <br...@clisp.org>
+
+	strtof: Revisit underflow behaviour.
+	* doc/posix-functions/strtof.texi: Mention the macOS bug. Mention the
+	mingw overflow bug. Mention the underflow bugs on Cygwin 2.9 and mingw.
+	Mention that gradual underflow does not count as an error on Cygwin 2.9,
+	mingw, MSVC.
+	* m4/strtof.m4 (gl_FUNC_STRTOF): Test against the mingw overflow bug.
+	* tests/test-strtof.h (test_function): Add a gradual underflow test.
+	Check the sign in case of flush-to-zero underflow.
+
 2024-07-23  Bruno Haible  <br...@clisp.org>
 
 	strtof, strtod, strtold: Fix underflow behaviour of system function.
diff --git a/doc/posix-functions/strtof.texi b/doc/posix-functions/strtof.texi
index b7bf64e934..cffe033cf0 100644
--- a/doc/posix-functions/strtof.texi
+++ b/doc/posix-functions/strtof.texi
@@ -19,7 +19,22 @@
 
 @item
 This function fails to parse @samp{NaN()} on some platforms:
-glibc-2.5, FreeBSD 6.2.
+glibc 2.5, FreeBSD 6.2.
+
+@item
+This function misparses @samp{nan(} on some platforms:
+macOS 10.6.6.
+
+@item
+This function fails to set @code{errno} upon overflow on some platforms:
+mingw 5.0.
+
+@item
+@c The term "underflow", as defined by ISO C23 § 7.12.1.(6), includes both
+@c "gradual underflow" (result is a denormalized number) and "flush-to-zero
+@c underflow" (result is zero).
+This function fails to set @code{errno} upon underflow on some platforms:
+Cygwin 2.9, mingw 5.0.
 @end itemize
 
 Portability problems not fixed by Gnulib:
@@ -39,6 +54,11 @@
 platforms:
 Mac OS X 10.5, FreeBSD 6.2, NetBSD 5.0, Cygwin, mingw, MSVC 14.
 
+@item
+This function fails to set @code{errno} upon gradual underflow (resulting
+in a denormalized number) on some platforms:
+MSVC 14.
+
 @item
 The replacement function does not always return correctly rounded results.
 @end itemize
diff --git a/m4/strtof.m4 b/m4/strtof.m4
index e37310ebde..71516f1bf6 100644
--- a/m4/strtof.m4
+++ b/m4/strtof.m4
@@ -1,5 +1,5 @@
 # strtof.m4
-# serial 3
+# serial 4
 dnl Copyright (C) 2002-2003, 2006-2024 Free Software Foundation, Inc.
 dnl This file is free software; the Free Software Foundation
 dnl gives unlimited permission to copy and/or distribute it,
@@ -83,6 +83,17 @@ AC_DEFUN([gl_FUNC_STRTOF]
     if (value != HUGE_VAL || term != (string + 3) || errno)
       result |= 8;
   }
+  {
+    /* In mingw 5.0 without __USE_MINGW_ANSI_STDIO and __USE_MINGW_STRTOX,
+       strtof does not set errno upon overflow.  */
+    const char *string = "1e50";
+    char *term;
+    float value;
+    errno = 0;
+    value = strtof (string, &term);
+    if (value != HUGE_VAL || term != (string + 4) || errno != ERANGE)
+      result |= 8;
+  }
   {
     /* glibc 2.7 and cygwin 1.5.24 misparse "nan()".  */
     const char *string = "nan()";
@@ -92,7 +103,7 @@ AC_DEFUN([gl_FUNC_STRTOF]
       result |= 16;
   }
   {
-    /* darwin 10.6.1 misparses "nan(".  */
+    /* Darwin 10.6.1 (macOS 10.6.6) misparses "nan(".  */
     const char *string = "nan(";
     char *term;
     float value = strtof (string, &term);
diff --git a/tests/test-strtof.h b/tests/test-strtof.h
index 1a7588f608..2ac6b898da 100644
--- a/tests/test-strtof.h
+++ b/tests/test-strtof.h
@@ -481,7 +481,7 @@ test_function (float (*my_strtof) (const char *, char **))
     ASSERT (errno == 0);
   }
 
-  /* Overflow/underflow.  */
+  /* Overflow.  */
   {
     const char input[] = "1E1000000";
     char *ptr;
@@ -502,6 +502,34 @@ test_function (float (*my_strtof) (const char *, char **))
     ASSERT (ptr == input + 10);
     ASSERT (errno == ERANGE);
   }
+
+  /* Gradual underflow, resulting in a denormalized number.  */
+  {
+    const char input[] = "1e-40";
+    char *ptr;
+    float result;
+    errno = 0;
+    result = my_strtof (input, &ptr);
+    ASSERT (0.0f < result && result <= FLT_MIN);
+    ASSERT (ptr == input + 5);
+#if !defined _MSC_VER
+    ASSERT (errno == ERANGE);
+#endif
+  }
+  {
+    const char input[] = "-1e-40";
+    char *ptr;
+    float result;
+    errno = 0;
+    result = my_strtof (input, &ptr);
+    ASSERT (-FLT_MIN <= result && result < 0.0f);
+    ASSERT (ptr == input + 6);
+#if !defined _MSC_VER
+    ASSERT (errno == ERANGE);
+#endif
+  }
+
+  /* Flush-to-zero underflow.  */
   {
     const char input[] = "1E-100000";
     char *ptr;
@@ -520,16 +548,14 @@ test_function (float (*my_strtof) (const char *, char **))
     errno = 0;
     result = my_strtof (input, &ptr);
     ASSERT (-FLT_MIN <= result && result <= 0.0f);
-#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.  */
+    /* Negative underflow.  Expect a negative sign, although POSIX allows +0.0f.
+       See also <https://sourceware.org/bugzilla/show_bug.cgi?id=5995>.  */
     ASSERT (!!signbit (result) == !!signbit (minus_zerof)); /* glibc-2.3.6, mingw */
-#endif
     ASSERT (ptr == input + 10);
     ASSERT (errno == ERANGE);
   }
+
+  /* Space before the exponent.  */
   {
     const char input[] = "1E 1000000";
     char *ptr;
-- 
2.34.1

From bd61e8c8ca7a41ce6e265c2cd8d9de5f4c3c4bd5 Mon Sep 17 00:00:00 2001
From: Bruno Haible <br...@clisp.org>
Date: Tue, 23 Jul 2024 12:40:37 +0200
Subject: [PATCH 5/7] strtod: Revisit underflow behaviour.

* doc/posix-functions/strtod.texi: Mention the macOS bug. Mention that
gradual underflow does not count as an error on Cygwin 2.9 and MSVC.
* m4/strtod.m4 (gl_FUNC_STRTOD): Update comment.
* tests/test-strtod.h (test_function): Add a gradual underflow test.
Check the sign in case of flush-to-zero underflow.
---
 ChangeLog                       |  7 ++++++
 doc/posix-functions/strtod.texi | 21 +++++++++++++++--
 m4/strtod.m4                    |  4 ++--
 tests/test-strtod.h             | 40 +++++++++++++++++++++++++++------
 4 files changed, 61 insertions(+), 11 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 3da7b01049..3f086d2a9f 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,12 @@
 2024-07-23  Bruno Haible  <br...@clisp.org>
 
+	strtod: Revisit underflow behaviour.
+	* doc/posix-functions/strtod.texi: Mention the macOS bug. Mention that
+	gradual underflow does not count as an error on Cygwin 2.9 and MSVC.
+	* m4/strtod.m4 (gl_FUNC_STRTOD): Update comment.
+	* tests/test-strtod.h (test_function): Add a gradual underflow test.
+	Check the sign in case of flush-to-zero underflow.
+
 	strtof: Revisit underflow behaviour.
 	* doc/posix-functions/strtof.texi: Mention the macOS bug. Mention the
 	mingw overflow bug. Mention the underflow bugs on Cygwin 2.9 and mingw.
diff --git a/doc/posix-functions/strtod.texi b/doc/posix-functions/strtod.texi
index 9b470eb465..a7d0143a13 100644
--- a/doc/posix-functions/strtod.texi
+++ b/doc/posix-functions/strtod.texi
@@ -31,7 +31,7 @@
 
 @item
 This function fails to parse @samp{NaN()} on some platforms:
-glibc-2.3.6, Mac OS X 10.5, FreeBSD 6.2, OpenBSD 4.0, AIX 7.1, HP-UX 11.11, Cygwin < 1.5.25-11, mingw, MSVC 14.
+glibc 2.3.6, Mac OS X 10.5, FreeBSD 6.2, OpenBSD 4.0, AIX 7.1, HP-UX 11.11, Cygwin < 1.5.25-11, mingw, MSVC 14.
 
 @item
 This function fails to parse @samp{NaN(@var{n-char-sequence})} on some
@@ -41,7 +41,11 @@
 @item
 This function parses @samp{NaN(@var{n-char-sequence})}, but returns
 the wrong end pointer on some platforms:
-glibc-2.4, AIX 7.1.
+glibc 2.4, AIX 7.1.
+
+@item
+This function misparses @samp{nan(} on some platforms:
+macOS 10.6.6.
 
 @item
 This function fails to parse C99 hexadecimal floating point on some
@@ -57,6 +61,14 @@
 This function returns the wrong end pointer for @samp{0x1p} on some
 platforms:
 AIX 7.1.
+
+@item
+@c The term "underflow", as defined by ISO C23 § 7.12.1.(6), includes both
+@c "gradual underflow" (result is a denormalized number) and "flush-to-zero
+@c underflow" (result is zero).
+This function fails to set @code{errno} upon gradual underflow (resulting
+in a denormalized number) on some platforms:
+Cygwin 2.9.
 @end itemize
 
 Portability problems fixed by Gnulib module @code{strtod-obsolete}:
@@ -82,6 +94,11 @@
 platforms:
 Mac OS X 10.5, FreeBSD 6.2, NetBSD 5.0, OpenBSD 4.0, Cygwin, mingw, MSVC 14.
 
+@item
+This function fails to set @code{errno} upon gradual underflow (resulting
+in a denormalized number) on some platforms:
+MSVC 14.
+
 @item
 The replacement function does not always return correctly rounded results.
 @end itemize
diff --git a/m4/strtod.m4 b/m4/strtod.m4
index 2ccbf3139a..e974478845 100644
--- a/m4/strtod.m4
+++ b/m4/strtod.m4
@@ -1,5 +1,5 @@
 # strtod.m4
-# serial 30
+# serial 31
 dnl Copyright (C) 2002-2003, 2006-2024 Free Software Foundation, Inc.
 dnl This file is free software; the Free Software Foundation
 dnl gives unlimited permission to copy and/or distribute it,
@@ -92,7 +92,7 @@ AC_DEFUN([gl_FUNC_STRTOD]
       result |= 16;
   }
   {
-    /* darwin 10.6.1 misparses "nan(".  */
+    /* Darwin 10.6.1 (macOS 10.6.6) misparses "nan(".  */
     const char *string = "nan(";
     char *term;
     double value = strtod (string, &term);
diff --git a/tests/test-strtod.h b/tests/test-strtod.h
index 48d784f368..c806953e84 100644
--- a/tests/test-strtod.h
+++ b/tests/test-strtod.h
@@ -481,7 +481,7 @@ test_function (double (*my_strtod) (const char *, char **))
     ASSERT (errno == 0);
   }
 
-  /* Overflow/underflow.  */
+  /* Overflow.  */
   {
     const char input[] = "1E1000000";
     char *ptr;
@@ -502,6 +502,34 @@ test_function (double (*my_strtod) (const char *, char **))
     ASSERT (ptr == input + 10);
     ASSERT (errno == ERANGE);
   }
+
+  /* Gradual underflow, resulting in a denormalized number.  */
+  {
+    const char input[] = "1e-320";
+    char *ptr;
+    double result;
+    errno = 0;
+    result = my_strtod (input, &ptr);
+    ASSERT (0.0 < result && result <= DBL_MIN);
+    ASSERT (ptr == input + 6);
+#if !defined _MSC_VER
+    ASSERT (errno == ERANGE);
+#endif
+  }
+  {
+    const char input[] = "-1e-320";
+    char *ptr;
+    double result;
+    errno = 0;
+    result = my_strtod (input, &ptr);
+    ASSERT (-DBL_MIN <= result && result < 0.0);
+    ASSERT (ptr == input + 7);
+#if !defined _MSC_VER
+    ASSERT (errno == ERANGE);
+#endif
+  }
+
+  /* Flush-to-zero underflow.  */
   {
     const char input[] = "1E-100000";
     char *ptr;
@@ -520,16 +548,14 @@ test_function (double (*my_strtod) (const char *, char **))
     errno = 0;
     result = my_strtod (input, &ptr);
     ASSERT (-DBL_MIN <= result && result <= 0.0);
-#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.  */
+    /* 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)); /* glibc-2.3.6, mingw */
-#endif
     ASSERT (ptr == input + 10);
     ASSERT (errno == ERANGE);
   }
+
+  /* Space before the exponent.  */
   {
     const char input[] = "1E 1000000";
     char *ptr;
-- 
2.34.1

From 423119a9f2a1549cbb9ef941fd76cf7cf5bfe1ea Mon Sep 17 00:00:00 2001
From: Bruno Haible <br...@clisp.org>
Date: Tue, 23 Jul 2024 12:40:58 +0200
Subject: [PATCH 6/7] strtold: Revisit underflow behaviour.

* doc/posix-functions/strtold.texi: Mention broken mingw 5.0. Mention
that gradual underflow does not count as an error on MSVC.
* tests/test-strtold.h (test_function): Add a gradual underflow test.
Check the sign in case of flush-to-zero underflow.
---
 ChangeLog                        |  6 ++++
 doc/posix-functions/strtold.texi | 12 ++++++++
 tests/test-strtold.h             | 48 +++++++++++++++++++++++++++-----
 3 files changed, 59 insertions(+), 7 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 3f086d2a9f..b4d5b8f9f8 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,11 @@
 2024-07-23  Bruno Haible  <br...@clisp.org>
 
+	strtold: Revisit underflow behaviour.
+	* doc/posix-functions/strtold.texi: Mention broken mingw 5.0. Mention
+	that gradual underflow does not count as an error on MSVC.
+	* tests/test-strtold.h (test_function): Add a gradual underflow test.
+	Check the sign in case of flush-to-zero underflow.
+
 	strtod: Revisit underflow behaviour.
 	* doc/posix-functions/strtod.texi: Mention the macOS bug. Mention that
 	gradual underflow does not count as an error on Cygwin 2.9 and MSVC.
diff --git a/doc/posix-functions/strtold.texi b/doc/posix-functions/strtold.texi
index cafcead911..3aada2c405 100644
--- a/doc/posix-functions/strtold.texi
+++ b/doc/posix-functions/strtold.texi
@@ -16,6 +16,10 @@
 This function returns a struct, not a @code{long double}, on some platforms:
 HP-UX 11.31/hppa.
 
+@item
+This function always returns a wrong value on some platforms:
+mingw 5.0.
+
 @item
 This function allows whitespace between @samp{e} and the exponent on
 some platforms:
@@ -50,6 +54,9 @@
 HP-UX 11.31/ia64.
 
 @item
+@c The term "underflow", as defined by ISO C23 § 7.12.1.(6), includes both
+@c "gradual underflow" (result is a denormalized number) and "flush-to-zero
+@c underflow" (result is zero).
 This function fails to set @code{errno} upon underflow on some platforms:
 @c https://cygwin.com/ml/cygwin/2019-12/msg00072.html
 Cygwin 2.9.
@@ -61,6 +68,11 @@
 
 Portability problems not fixed by Gnulib:
 @itemize
+@item
+This function fails to set @code{errno} upon gradual underflow (resulting
+in a denormalized number) on some platforms:
+MSVC 14.
+
 @item
 The replacement function does not always return correctly rounded results.
 @end itemize
diff --git a/tests/test-strtold.h b/tests/test-strtold.h
index b5d79dc07b..bd12988c4c 100644
--- a/tests/test-strtold.h
+++ b/tests/test-strtold.h
@@ -481,7 +481,7 @@ test_function (long double (*my_strtold) (const char *, char **))
     ASSERT (errno == 0);
   }
 
-  /* Overflow/underflow.  */
+  /* Overflow.  */
   {
     const char input[] = "1E1000000";
     char *ptr;
@@ -502,6 +502,42 @@ test_function (long double (*my_strtold) (const char *, char **))
     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
+    char *ptr;
+    long double result;
+    errno = 0;
+    result = my_strtold (input, &ptr);
+    ASSERT (0.0L < result && result <= LDBL_MIN);
+    ASSERT (ptr == input + strlen (input));
+#if !defined _MSC_VER
+    ASSERT (errno == ERANGE);
+#endif
+  }
+  {
+#if LDBL_MAX_EXP > 10000
+    const char input[] = "-1e-4950";
+#else
+    const char input[] = "-1e-320";
+#endif
+    char *ptr;
+    long double result;
+    errno = 0;
+    result = my_strtold (input, &ptr);
+    ASSERT (-LDBL_MIN <= result && result < 0.0L);
+    ASSERT (ptr == input + strlen (input));
+#if !defined _MSC_VER
+    ASSERT (errno == ERANGE);
+#endif
+  }
+
+  /* Flush-to-zero underflow.  */
   {
     const char input[] = "1E-100000";
     char *ptr;
@@ -520,16 +556,14 @@ test_function (long double (*my_strtold) (const char *, char **))
     errno = 0;
     result = my_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.  */
+    /* 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)); /* glibc-2.3.2, Haiku */
-#endif
     ASSERT (ptr == input + 10);
     ASSERT (errno == ERANGE);
   }
+
+  /* Space before the exponent.  */
   {
     const char input[] = "1E 1000000";
     char *ptr;
-- 
2.34.1

>From a3b60438fb3a15add88aeceb85b1e4c8e98c7385 Mon Sep 17 00:00:00 2001
From: Bruno Haible <br...@clisp.org>
Date: Tue, 23 Jul 2024 12:41:12 +0200
Subject: [PATCH 7/7] strtof, strtod, strtold tests: Strengthen tests.

* tests/test-strtof.h (test_function): Add another overflow test.
* tests/test-strtod.h (test_function): Likewise.
* tests/test-strtold.h (test_function): Likewise.
---
 ChangeLog            |  7 +++++++
 tests/test-strtod.h  | 10 ++++++++++
 tests/test-strtof.h  | 10 ++++++++++
 tests/test-strtold.h | 10 ++++++++++
 4 files changed, 37 insertions(+)

diff --git a/ChangeLog b/ChangeLog
index b4d5b8f9f8..309f1d8ac0 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,10 @@
+2024-07-23  Bruno Haible  <br...@clisp.org>
+
+	strtof, strtod, strtold tests: Strengthen tests.
+	* tests/test-strtof.h (test_function): Add another overflow test.
+	* tests/test-strtod.h (test_function): Likewise.
+	* tests/test-strtold.h (test_function): Likewise.
+
 2024-07-23  Bruno Haible  <br...@clisp.org>
 
 	strtold: Revisit underflow behaviour.
diff --git a/tests/test-strtod.h b/tests/test-strtod.h
index c806953e84..211d9230c7 100644
--- a/tests/test-strtod.h
+++ b/tests/test-strtod.h
@@ -482,6 +482,16 @@ test_function (double (*my_strtod) (const char *, char **))
   }
 
   /* Overflow.  */
+  {
+    const char input[] = "1e500";
+    char *ptr;
+    double result;
+    errno = 0;
+    result = my_strtod (input, &ptr);
+    ASSERT (result == HUGE_VAL);
+    ASSERT (ptr == input + 5);          /* OSF/1 5.1 */
+    ASSERT (errno == ERANGE);
+  }
   {
     const char input[] = "1E1000000";
     char *ptr;
diff --git a/tests/test-strtof.h b/tests/test-strtof.h
index 2ac6b898da..fd04f07873 100644
--- a/tests/test-strtof.h
+++ b/tests/test-strtof.h
@@ -482,6 +482,16 @@ test_function (float (*my_strtof) (const char *, char **))
   }
 
   /* Overflow.  */
+  {
+    const char input[] = "1e50";
+    char *ptr;
+    float result;
+    errno = 0;
+    result = my_strtof (input, &ptr);
+    ASSERT (result == HUGE_VAL);
+    ASSERT (ptr == input + 4);          /* OSF/1 5.1 */
+    ASSERT (errno == ERANGE);
+  }
   {
     const char input[] = "1E1000000";
     char *ptr;
diff --git a/tests/test-strtold.h b/tests/test-strtold.h
index bd12988c4c..a872f8a882 100644
--- a/tests/test-strtold.h
+++ b/tests/test-strtold.h
@@ -482,6 +482,16 @@ test_function (long double (*my_strtold) (const char *, char **))
   }
 
   /* Overflow.  */
+  {
+    const char input[] = "1e6000";
+    char *ptr;
+    long double result;
+    errno = 0;
+    result = my_strtold (input, &ptr);
+    ASSERT (result == HUGE_VALL);
+    ASSERT (ptr == input + 6);
+    ASSERT (errno == ERANGE);
+  }
   {
     const char input[] = "1E1000000";
     char *ptr;
-- 
2.34.1

Reply via email to