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