summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEric Blake <ebb9@byu.net>2008-03-29 13:50:21 -0600
committerEric Blake <ebb9@byu.net>2008-03-29 19:16:57 -0600
commitde138ad63af1ad9cb74b16743c511eebf0ccfbaa (patch)
treec84f448b732d7af4b56fb4203754b085169a88a7
parent857523660ddabbf1e4bba1c6f5f322451991fa08 (diff)
downloadgnulib-de138ad63af1ad9cb74b16743c511eebf0ccfbaa.tar.gz
Document various strtod bugs, with some fixes.
* doc/posix-functions/strtod.texi (strtod): Document bugs with "-0x", "inf", "nan", and hex constants. * doc/posix-functions/atof.texi (atof): Likewise. * modules/stdlib (Makefile.am): Support strtod. * m4/stdlib_h.m4 (gl_STDLIB_H_DEFAULTS): Likewise. * m4/strtod.m4 (gl_FUNC_STRTOD): Fit in stdlib framework, and detect additional strtod bugs. * lib/stdlib.in.h (rpl_strtod): Add declarations. * lib/strtod.c (strtod): Return -0.0 on negative underflow. Use bool where appropriate. Parse 'inf' and 'nan'. * tests/test-strtod.c: New file. * modules/strtod (Depends-on): Add stdbool, stdlib. (configure.ac): Turn on module indicator. * modules/strtod-tests: New module. Signed-off-by: Eric Blake <ebb9@byu.net>
-rw-r--r--ChangeLog18
-rw-r--r--doc/posix-functions/atof.texi35
-rw-r--r--doc/posix-functions/strtod.texi38
-rw-r--r--lib/stdlib.in.h19
-rw-r--r--lib/strtod.c99
-rw-r--r--m4/stdlib_h.m47
-rw-r--r--m4/strtod.m442
-rw-r--r--modules/stdlib3
-rw-r--r--modules/strtod3
-rw-r--r--modules/strtod-tests14
-rw-r--r--tests/test-strtod.c822
11 files changed, 1065 insertions, 35 deletions
diff --git a/ChangeLog b/ChangeLog
index f6c7dc0d3d..1e0254d74c 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,23 @@
2008-03-29 Eric Blake <ebb9@byu.net>
+ Document various strtod bugs, with some fixes.
+ * doc/posix-functions/strtod.texi (strtod): Document bugs with
+ "-0x", "inf", "nan", and hex constants.
+ * doc/posix-functions/atof.texi (atof): Likewise.
+ * modules/stdlib (Makefile.am): Support strtod.
+ * m4/stdlib_h.m4 (gl_STDLIB_H_DEFAULTS): Likewise.
+ * m4/strtod.m4 (gl_FUNC_STRTOD): Fit in stdlib framework, and
+ detect additional strtod bugs.
+ * lib/stdlib.in.h (rpl_strtod): Add declarations.
+ * lib/strtod.c (strtod): Return -0.0 on negative underflow. Use
+ bool where appropriate. Parse 'inf' and 'nan'.
+ * tests/test-strtod.c: New file.
+ * modules/strtod (Depends-on): Add stdbool, stdlib.
+ (configure.ac): Turn on module indicator.
+ * modules/strtod-tests: New module.
+
+2008-03-29 Eric Blake <ebb9@byu.net>
+
Fix ftell on mingw.
* lib/ftell.c (EOVERFLOW): Define if the system lacks it.
* modules/ftell-tests (Depends-on): Add binary-io.
diff --git a/doc/posix-functions/atof.texi b/doc/posix-functions/atof.texi
index c17dc3a1e2..c20a60f68c 100644
--- a/doc/posix-functions/atof.texi
+++ b/doc/posix-functions/atof.texi
@@ -12,4 +12,39 @@ Portability problems fixed by Gnulib:
Portability problems not fixed by Gnulib:
@itemize
+@item
+This function mis-parses strings with leading @samp{+} on some old platforms:
+Old versions of Linux.
+
+@item
+This function returns a positive value for negative underflow on some
+platforms:
+glibc 2.4, Mingw, Cygwin.
+
+@item
+This function fails to do a valid parse of @samp{-0x} on some
+platforms:
+glibc 2.4, Cygwin < 1.5.25-11.
+
+@item
+This function fails to parse Infinities and plain NaNs on some platforms:
+Solaris 8, Mingw, OpenBSD 4.0.
+
+@item
+This function fails to parse NaN() on some platforms:
+Solaris 8, Mingw, OpenBSD 4.0, Cygwin < 1.5.25-11.
+
+@item
+This function fails to parse NaN(n-char-sequence) on some platforms:
+Solaris 8, Mingw, OpenBSD 4.0.
+
+@item
+This function fails to parse C99 hexadecimal floating point on some
+platforms:
+Solaris 8, Mingw, OpenBSD 4.0.
+
+@item
+This function fails to correctly parse very long strings on some
+platforms:
+Mingw, Cygwin.
@end itemize
diff --git a/doc/posix-functions/strtod.texi b/doc/posix-functions/strtod.texi
index 71e0978691..349802ab10 100644
--- a/doc/posix-functions/strtod.texi
+++ b/doc/posix-functions/strtod.texi
@@ -10,9 +10,11 @@ Portability problems fixed by Gnulib:
@itemize
@item
This function is missing on some old platforms.
+
@item
This function mis-parses strings with leading @samp{+} on some old platforms:
Old versions of Linux.
+
@item
This function returns a wrong end pointer on some platforms:
Solaris 2.4.
@@ -20,4 +22,40 @@ Solaris 2.4.
Portability problems not fixed by Gnulib:
@itemize
+@item
+This function returns a positive value for negative underflow on some
+platforms:
+glibc 2.4, Mingw, Cygwin.
+
+@item
+This function fails to do a valid parse of @samp{-0x} on some
+platforms:
+glibc 2.4, Cygwin < 1.5.25-11.
+
+@item
+This function fails to parse Infinities and plain NaNs on some platforms:
+Solaris 8, Mingw, OpenBSD 4.0.
+
+@item
+This function fails to parse NaN() on some platforms:
+Solaris 8, Mingw, OpenBSD 4.0, Cygwin < 1.5.25-11.
+
+@item
+This function fails to parse NaN(n-char-sequence) on some platforms:
+Solaris 8, Mingw, OpenBSD 4.0.
+
+@item
+This function returns the wrong end pointer when parsing
+NaN(n-char-sequence) on some platforms:
+glibc 2.4.
+
+@item
+This function fails to parse C99 hexadecimal floating point on some
+platforms:
+Solaris 8, Mingw, OpenBSD 4.0.
+
+@item
+This function fails to correctly parse very long strings on some
+platforms:
+Mingw, Cygwin.
@end itemize
diff --git a/lib/stdlib.in.h b/lib/stdlib.in.h
index 100ff52652..0181dd2376 100644
--- a/lib/stdlib.in.h
+++ b/lib/stdlib.in.h
@@ -1,6 +1,6 @@
/* A GNU-like <stdlib.h>.
- Copyright (C) 1995, 2001-2004, 2006-2007 Free Software Foundation, Inc.
+ Copyright (C) 1995, 2001-2004, 2006-2008 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
@@ -199,6 +199,23 @@ extern int unsetenv (const char *name);
#endif
+#if @GNULIB_STRTOD@
+# if @REPLACE_STRTOD@
+# define strtod rpl_strtod
+# endif
+# if !@HAVE_STRTOD@ || @REPLACE_STRTOD@
+ /* Parse a double from STRING, updating ENDP if appropriate. */
+extern double strtod (const char *str, char **endp);
+# endif
+#elif defined GNULIB_POSIXCHECK
+# undef strtod
+# define strtod(s, e) \
+ (GL_LINK_WARNING ("strtod is unportable - " \
+ "use gnulib module strtod for portability"), \
+ strtod (s, e))
+#endif
+
+
#ifdef __cplusplus
}
#endif
diff --git a/lib/strtod.c b/lib/strtod.c
index bba537af28..147a5bf634 100644
--- a/lib/strtod.c
+++ b/lib/strtod.c
@@ -1,4 +1,5 @@
-/* Copyright (C) 1991, 1992, 1997, 1999, 2003, 2006 Free Software Foundation, Inc.
+/* Copyright (C) 1991, 1992, 1997, 1999, 2003, 2006, 2008 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
@@ -15,14 +16,13 @@
#include <config.h>
-#include <errno.h>
+#include <stdlib.h>
#include <ctype.h>
-
-#include <math.h>
-
+#include <errno.h>
#include <float.h>
-#include <stdlib.h>
+#include <math.h>
+#include <stdbool.h>
#include <string.h>
/* Convert NPTR to a double. If ENDPTR is not NULL, a pointer to the
@@ -30,14 +30,14 @@
double
strtod (const char *nptr, char **endptr)
{
- register const char *s;
- short int sign;
+ const char *s;
+ bool negative = false;
/* The number so far. */
double num;
- int got_dot; /* Found a decimal point. */
- int got_digit; /* Seen any digits. */
+ bool got_dot; /* Found a decimal point. */
+ bool got_digit; /* Seen any digits. */
/* The exponent of the number. */
long int exponent;
@@ -55,19 +55,19 @@ strtod (const char *nptr, char **endptr)
++s;
/* Get the sign. */
- sign = *s == '-' ? -1 : 1;
+ negative = *s == '-';
if (*s == '-' || *s == '+')
++s;
num = 0.0;
- got_dot = 0;
- got_digit = 0;
+ got_dot = false;
+ got_digit = false;
exponent = 0;
for (;; ++s)
{
if ('0' <= *s && *s <= '9')
{
- got_digit = 1;
+ got_digit = true;
/* Make sure that multiplication by 10 will not overflow. */
if (num > DBL_MAX * 0.1)
@@ -89,14 +89,54 @@ strtod (const char *nptr, char **endptr)
}
else if (!got_dot && *s == '.')
/* Record that we have found the decimal point. */
- got_dot = 1;
+ got_dot = true;
else
/* Any other character terminates the number. */
break;
}
if (!got_digit)
- goto noconv;
+ {
+ /* Check for infinities and NaNs. */
+ if (tolower ((unsigned char) *s) == 'i'
+ && tolower ((unsigned char) s[1]) == 'n'
+ && tolower ((unsigned char) s[2]) == 'f')
+ {
+ s += 3;
+ num = HUGE_VAL;
+ if (tolower ((unsigned char) *s) == 'i'
+ && tolower ((unsigned char) s[1]) == 'n'
+ && tolower ((unsigned char) s[2]) == 'i'
+ && tolower ((unsigned char) s[3]) == 't'
+ && tolower ((unsigned char) s[4]) == 'y')
+ s += 5;
+ goto valid;
+ }
+#ifdef NAN
+ else if (tolower ((unsigned char) *s) == 'n'
+ && tolower ((unsigned char) s[1]) == 'a'
+ && tolower ((unsigned char) s[2]) == 'n')
+ {
+ s += 3;
+ num = NAN;
+ /* Since nan(<n-char-sequence>) is implementation-defined,
+ we define it by ignoring <n-char-sequence>. A nicer
+ implementation would populate the bits of the NaN
+ according to interpreting n-char-sequence as a
+ hexadecimal number, but the result is still a NaN. */
+ if (*s == '(')
+ {
+ const char *p = s + 1;
+ while (isalnum ((unsigned char) *p))
+ p++;
+ if (*p == ')')
+ s = p + 1;
+ }
+ goto valid;
+ }
+#endif
+ goto noconv;
+ }
if (tolower ((unsigned char) *s) == 'e')
{
@@ -108,7 +148,7 @@ strtod (const char *nptr, char **endptr)
errno = 0;
++s;
exp = strtol (s, &end, 10);
- if (errno == ERANGE)
+ if (errno == ERANGE && num)
{
/* The exponent overflowed a `long int'. It is probably a safe
assumption that an exponent that cannot be represented by
@@ -129,11 +169,8 @@ strtod (const char *nptr, char **endptr)
exponent += exp;
}
- if (endptr != NULL)
- *endptr = (char *) s;
-
if (num == 0.0)
- return 0.0;
+ goto valid;
/* Multiply NUM by 10 to the EXPONENT power,
checking for overflow and underflow. */
@@ -151,23 +188,29 @@ strtod (const char *nptr, char **endptr)
num *= pow (10.0, (double) exponent);
- return num * sign;
+ valid:
+ if (endptr != NULL)
+ *endptr = (char *) s;
+ return negative ? -num : num;
-overflow:
+ overflow:
/* Return an overflow error. */
+ if (endptr != NULL)
+ *endptr = (char *) s;
errno = ERANGE;
- return HUGE_VAL * sign;
+ return negative ? -HUGE_VAL : HUGE_VAL;
-underflow:
+ underflow:
/* Return an underflow error. */
if (endptr != NULL)
- *endptr = (char *) nptr;
+ *endptr = (char *) s;
errno = ERANGE;
- return 0.0;
+ return negative ? -0.0 : 0.0;
-noconv:
+ noconv:
/* There was no number. */
if (endptr != NULL)
*endptr = (char *) nptr;
+ errno = EINVAL;
return 0.0;
}
diff --git a/m4/stdlib_h.m4 b/m4/stdlib_h.m4
index fe4ce122e9..d9240b46a0 100644
--- a/m4/stdlib_h.m4
+++ b/m4/stdlib_h.m4
@@ -1,5 +1,5 @@
-# stdlib_h.m4 serial 5
-dnl Copyright (C) 2007 Free Software Foundation, Inc.
+# stdlib_h.m4 serial 6
+dnl Copyright (C) 2007, 2008 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
@@ -27,6 +27,7 @@ AC_DEFUN([gl_STDLIB_H_DEFAULTS],
GNULIB_MKSTEMP=0; AC_SUBST([GNULIB_MKSTEMP])
GNULIB_PUTENV=0; AC_SUBST([GNULIB_PUTENV])
GNULIB_SETENV=0; AC_SUBST([GNULIB_SETENV])
+ GNULIB_STRTOD=0; AC_SUBST([GNULIB_STRTOD])
GNULIB_UNSETENV=0; AC_SUBST([GNULIB_UNSETENV])
dnl Assume proper GNU behavior unless another module says otherwise.
HAVE_CALLOC_POSIX=1; AC_SUBST([HAVE_CALLOC_POSIX])
@@ -35,8 +36,10 @@ AC_DEFUN([gl_STDLIB_H_DEFAULTS],
HAVE_MKDTEMP=1; AC_SUBST([HAVE_MKDTEMP])
HAVE_REALLOC_POSIX=1; AC_SUBST([HAVE_REALLOC_POSIX])
HAVE_SETENV=1; AC_SUBST([HAVE_SETENV])
+ HAVE_STRTOD=1; AC_SUBST([HAVE_STRTOD])
HAVE_UNSETENV=1; AC_SUBST([HAVE_UNSETENV])
REPLACE_MKSTEMP=0; AC_SUBST([REPLACE_MKSTEMP])
REPLACE_PUTENV=0; AC_SUBST([REPLACE_PUTENV])
+ REPLACE_STRTOD=0; AC_SUBST([REPLACE_STRTOD])
VOID_UNSETENV=0; AC_SUBST([VOID_UNSETENV])
])
diff --git a/m4/strtod.m4 b/m4/strtod.m4
index 1de2f2fb97..7a10a21fcd 100644
--- a/m4/strtod.m4
+++ b/m4/strtod.m4
@@ -1,17 +1,51 @@
-# strtod.m4 serial 6
-dnl Copyright (C) 2002, 2003, 2006, 2007 Free Software Foundation, Inc.
+# strtod.m4 serial 7
+dnl Copyright (C) 2002, 2003, 2006, 2007, 2008 Free Software
+dnl Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
AC_DEFUN([gl_FUNC_STRTOD],
[
+ AC_REQUIRE([gl_STDLIB_H_DEFAULTS])
AC_FUNC_STRTOD
dnl Note: AC_FUNC_STRTOD does AC_LIBOBJ(strtod).
if test $ac_cv_func_strtod = no; then
- AC_DEFINE(strtod, rpl_strtod,
- [Define to rpl_strtod if the replacement function should be used.])
+ HAVE_STRTOD=0
+ REPLACE_STRTOD=1
gl_PREREQ_STRTOD
+ else
+ AC_CACHE_CHECK([whether strtod obeys C99], [gl_cv_func_strtod_works],
+ [AC_RUN_IFELSE([AC_LANG_PROGRAM([[
+#include <stdlib.h>
+#include <math.h>
+]], [[
+ {
+ /* Older glibc and Cygwin mis-parse "-0x". */
+ char *string = "-0x";
+ char *term;
+ double value = strtod (string, &term);
+ if (1 / value != -HUGE_VAL || term != (string + 2))
+ return 1;
+ }
+ {
+ /* Many platforms do not parse infinities. */
+ char *string = "inf";
+ char *term;
+ double value = strtod (string, &term);
+ if (value != HUGE_VAL || term != (string + 3))
+ return 1;
+ }
+]])],
+ [gl_cv_func_strtod_works=yes],
+ [gl_cv_func_strtod_works=no],
+ [gl_cv_func_strtod_works="guessing no"])])
+ if test "$gl_cv_func_strtod_works" != yes; then
+ REPLACE_STRTOD=1
+ gl_PREREQ_STRTOD
+ dnl Use undocumented macro to set POW_LIB correctly.
+ _AC_LIBOBJ_STRTOD
+ fi
fi
])
diff --git a/modules/stdlib b/modules/stdlib
index d9a6e9788a..b1195301ce 100644
--- a/modules/stdlib
+++ b/modules/stdlib
@@ -31,6 +31,7 @@ stdlib.h: stdlib.in.h
-e 's|@''GNULIB_MKSTEMP''@|$(GNULIB_MKSTEMP)|g' \
-e 's|@''GNULIB_PUTENV''@|$(GNULIB_PUTENV)|g' \
-e 's|@''GNULIB_SETENV''@|$(GNULIB_SETENV)|g' \
+ -e 's|@''GNULIB_STRTOD''@|$(GNULIB_STRTOD)|g' \
-e 's|@''GNULIB_UNSETENV''@|$(GNULIB_UNSETENV)|g' \
-e 's|@''HAVE_CALLOC_POSIX''@|$(HAVE_CALLOC_POSIX)|g' \
-e 's|@''HAVE_GETSUBOPT''@|$(HAVE_GETSUBOPT)|g' \
@@ -38,9 +39,11 @@ stdlib.h: stdlib.in.h
-e 's|@''HAVE_MKDTEMP''@|$(HAVE_MKDTEMP)|g' \
-e 's|@''HAVE_REALLOC_POSIX''@|$(HAVE_REALLOC_POSIX)|g' \
-e 's|@''HAVE_SETENV''@|$(HAVE_SETENV)|g' \
+ -e 's|@''HAVE_STRTOD''@|$(HAVE_STRTOD)|g' \
-e 's|@''HAVE_UNSETENV''@|$(HAVE_UNSETENV)|g' \
-e 's|@''REPLACE_MKSTEMP''@|$(REPLACE_MKSTEMP)|g' \
-e 's|@''REPLACE_PUTENV''@|$(REPLACE_PUTENV)|g' \
+ -e 's|@''REPLACE_STRTOD''@|$(REPLACE_STRTOD)|g' \
-e 's|@''VOID_UNSETENV''@|$(VOID_UNSETENV)|g' \
-e '/definition of GL_LINK_WARNING/r $(LINK_WARNING_H)' \
< $(srcdir)/stdlib.in.h; \
diff --git a/modules/strtod b/modules/strtod
index 4098b13641..5f2dce5546 100644
--- a/modules/strtod
+++ b/modules/strtod
@@ -6,9 +6,12 @@ lib/strtod.c
m4/strtod.m4
Depends-on:
+stdbool
+stdlib
configure.ac:
gl_FUNC_STRTOD
+gl_STDLIB_MODULE_INDICATOR([strtod])
Makefile.am:
LIBS += $(POW_LIB)
diff --git a/modules/strtod-tests b/modules/strtod-tests
new file mode 100644
index 0000000000..e40a274d6f
--- /dev/null
+++ b/modules/strtod-tests
@@ -0,0 +1,14 @@
+Files:
+tests/test-strtod.c
+
+Depends-on:
+float
+isnand-nolibm
+signbit
+
+configure.ac:
+
+Makefile.am:
+LIBS += $(POW_LIB)
+TESTS += test-strtod
+check_PROGRAMS += test-strtod
diff --git a/tests/test-strtod.c b/tests/test-strtod.c
new file mode 100644
index 0000000000..c54a8fda73
--- /dev/null
+++ b/tests/test-strtod.c
@@ -0,0 +1,822 @@
+/*
+ * Copyright (C) 2008 Free Software Foundation
+ * Written by Eric Blake
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+
+#include <stdlib.h>
+
+#include <errno.h>
+#include <float.h>
+#include <math.h>
+#include <stdio.h>
+#include <string.h>
+
+#define ASSERT(expr) \
+ do \
+ { \
+ if (!(expr)) \
+ { \
+ fprintf (stderr, "%s:%d: assertion failed\n", __FILE__, __LINE__); \
+ /* FIXME abort ();*/status = 1; \
+ } \
+ } \
+ while (0)
+
+int
+main ()
+{
+ int status = 0;
+ /* Subject sequence empty or invalid. */
+ {
+ errno = 0;
+ const char input[] = "";
+ char *ptr;
+ double result = strtod (input, &ptr);
+ ASSERT (result == 0.0);
+ ASSERT (!signbit (result));
+ ASSERT (ptr == input);
+ ASSERT (errno == 0 || errno == EINVAL);
+ }
+ {
+ errno = 0;
+ const char input[] = " ";
+ char *ptr;
+ double result = strtod (input, &ptr);
+ ASSERT (result == 0.0);
+ ASSERT (!signbit (result));
+ ASSERT (ptr == input);
+ ASSERT (errno == 0 || errno == EINVAL);
+ }
+ {
+ errno = 0;
+ const char input[] = " +";
+ char *ptr;
+ double result = strtod (input, &ptr);
+ ASSERT (result == 0.0);
+ ASSERT (!signbit (result));
+ ASSERT (ptr == input);
+ ASSERT (errno == 0 || errno == EINVAL);
+ }
+ {
+ errno = 0;
+ const char input[] = " .";
+ char *ptr;
+ double result = strtod (input, &ptr);
+ ASSERT (result == 0.0);
+ ASSERT (!signbit (result));
+ ASSERT (ptr == input);
+ ASSERT (errno == 0 || errno == EINVAL);
+ }
+ {
+ errno = 0;
+ const char input[] = " .e0";
+ char *ptr;
+ double result = strtod (input, &ptr);
+ ASSERT (result == 0.0);
+ ASSERT (!signbit (result));
+ ASSERT (ptr == input);
+ ASSERT (errno == 0 || errno == EINVAL);
+ }
+ {
+ errno = 0;
+ const char input[] = " +.e-0";
+ char *ptr;
+ double result = strtod (input, &ptr);
+ ASSERT (result == 0.0);
+ ASSERT (!signbit (result));
+ ASSERT (ptr == input);
+ ASSERT (errno == 0 || errno == EINVAL);
+ }
+ {
+ errno = 0;
+ const char input[] = " in";
+ char *ptr;
+ double result = strtod (input, &ptr);
+ ASSERT (result == 0.0);
+ ASSERT (!signbit (result));
+ ASSERT (ptr == input);
+ ASSERT (errno == 0 || errno == EINVAL);
+ }
+ {
+ errno = 0;
+ const char input[] = " na";
+ char *ptr;
+ double result = strtod (input, &ptr);
+ ASSERT (result == 0.0);
+ ASSERT (!signbit (result));
+ ASSERT (ptr == input);
+ ASSERT (errno == 0 || errno == EINVAL);
+ }
+
+ /* Simple floating point values. */
+ {
+ errno = 0;
+ const char input[] = "1";
+ char *ptr;
+ double result = strtod (input, &ptr);
+ ASSERT (result == 1.0);
+ ASSERT (ptr == input + 1);
+ ASSERT (errno == 0);
+ }
+ {
+ errno = 0;
+ const char input[] = "1.";
+ char *ptr;
+ double result = strtod (input, &ptr);
+ ASSERT (result == 1.0);
+ ASSERT (ptr == input + 2);
+ ASSERT (errno == 0);
+ }
+ {
+ errno = 0;
+ const char input[] = ".1";
+ char *ptr;
+ double result = strtod (input, &ptr);
+ ASSERT (result == 0.1);
+ ASSERT (ptr == input + 2);
+ ASSERT (errno == 0);
+ }
+ {
+ errno = 0;
+ const char input[] = " 1";
+ char *ptr;
+ double result = strtod (input, &ptr);
+ ASSERT (result == 1.0);
+ ASSERT (ptr == input + 2);
+ ASSERT (errno == 0);
+ }
+ {
+ errno = 0;
+ const char input[] = "+1";
+ char *ptr;
+ double result = strtod (input, &ptr);
+ ASSERT (result == 1.0);
+ ASSERT (ptr == input + 2);
+ ASSERT (errno == 0);
+ }
+ {
+ errno = 0;
+ const char input[] = "-1";
+ char *ptr;
+ double result = strtod (input, &ptr);
+ ASSERT (result == -1.0);
+ ASSERT (ptr == input + 2);
+ ASSERT (errno == 0);
+ }
+ {
+ errno = 0;
+ const char input[] = "1e0";
+ char *ptr;
+ double result = strtod (input, &ptr);
+ ASSERT (result == 1.0);
+ ASSERT (ptr == input + 3);
+ ASSERT (errno == 0);
+ }
+ {
+ errno = 0;
+ const char input[] = "1e+0";
+ char *ptr;
+ double result = strtod (input, &ptr);
+ ASSERT (result == 1.0);
+ ASSERT (ptr == input + 4);
+ ASSERT (errno == 0);
+ }
+ {
+ errno = 0;
+ const char input[] = "1e-0";
+ char *ptr;
+ double result = strtod (input, &ptr);
+ ASSERT (result == 1.0);
+ ASSERT (ptr == input + 4);
+ ASSERT (errno == 0);
+ }
+ {
+ errno = 0;
+ const char input[] = "1e1";
+ char *ptr;
+ double result = strtod (input, &ptr);
+ ASSERT (result == 10.0);
+ ASSERT (ptr == input + 3);
+ ASSERT (errno == 0);
+ }
+ {
+ errno = 0;
+ const char input[] = "1e-1";
+ char *ptr;
+ double result = strtod (input, &ptr);
+ ASSERT (result == 0.1);
+ ASSERT (ptr == input + 4);
+ ASSERT (errno == 0);
+ }
+
+ /* Zero. */
+ {
+ errno = 0;
+ const char input[] = "0";
+ char *ptr;
+ double result = strtod (input, &ptr);
+ ASSERT (result == 0.0);
+ ASSERT (!signbit (result));
+ ASSERT (ptr == input + 1);
+ ASSERT (errno == 0);
+ }
+ {
+ errno = 0;
+ const char input[] = ".0";
+ char *ptr;
+ double result = strtod (input, &ptr);
+ ASSERT (result == 0.0);
+ ASSERT (!signbit (result));
+ ASSERT (ptr == input + 2);
+ ASSERT (errno == 0);
+ }
+ {
+ errno = 0;
+ const char input[] = "0e0";
+ char *ptr;
+ double result = strtod (input, &ptr);
+ ASSERT (result == 0.0);
+ ASSERT (!signbit (result));
+ ASSERT (ptr == input + 3);
+ ASSERT (errno == 0);
+ }
+ {
+ errno = 0;
+ const char input[] = "0e+9999999";
+ char *ptr;
+ double result = strtod (input, &ptr);
+ ASSERT (result == 0.0);
+ ASSERT (!signbit (result));
+ ASSERT (ptr == input + 10);
+ ASSERT (errno == 0);
+ }
+ {
+ errno = 0;
+ const char input[] = "0e-9999999";
+ char *ptr;
+ double result = strtod (input, &ptr);
+ ASSERT (result == 0.0);
+ ASSERT (!signbit (result));
+ ASSERT (ptr == input + 10);
+ ASSERT (errno == 0);
+ }
+ {
+ errno = 0;
+ const char input[] = "-0";
+ char *ptr;
+ double result = strtod (input, &ptr);
+ ASSERT (result == 0.0);
+ ASSERT (signbit (result) == signbit (-0.0));
+ ASSERT (ptr == input + 2);
+ ASSERT (errno == 0);
+ }
+
+ /* Suffixes. */
+ {
+ errno = 0;
+ const char input[] = "1f";
+ char *ptr;
+ double result = strtod (input, &ptr);
+ ASSERT (result == 1.0);
+ ASSERT (ptr == input + 1);
+ ASSERT (errno == 0);
+ }
+ {
+ errno = 0;
+ const char input[] = "1.f";
+ char *ptr;
+ double result = strtod (input, &ptr);
+ ASSERT (result == 1.0);
+ ASSERT (ptr == input + 2);
+ ASSERT (errno == 0);
+ }
+ {
+ errno = 0;
+ const char input[] = "1e";
+ char *ptr;
+ double result = strtod (input, &ptr);
+ ASSERT (result == 1.0);
+ ASSERT (ptr == input + 1);
+ ASSERT (errno == 0);
+ }
+ {
+ errno = 0;
+ const char input[] = "1e+";
+ char *ptr;
+ double result = strtod (input, &ptr);
+ ASSERT (result == 1.0);
+ ASSERT (ptr == input + 1);
+ ASSERT (errno == 0);
+ }
+ {
+ errno = 0;
+ const char input[] = "1e-";
+ char *ptr;
+ double result = strtod (input, &ptr);
+ ASSERT (result == 1.0);
+ ASSERT (ptr == input + 1);
+ ASSERT (errno == 0);
+ }
+ {
+ errno = 0;
+ const char input[] = "0x";
+ char *ptr;
+ double result = strtod (input, &ptr);
+ ASSERT (result == 0.0);
+ ASSERT (!signbit (result));
+ ASSERT (ptr == input + 1);
+ ASSERT (errno == 0);
+ }
+ {
+ errno = 0;
+ const char input[] = "00x1";
+ char *ptr;
+ double result = strtod (input, &ptr);
+ ASSERT (result == 0.0);
+ ASSERT (!signbit (result));
+ ASSERT (ptr == input + 2);
+ ASSERT (errno == 0);
+ }
+ {
+ errno = 0;
+ const char input[] = "-0x";
+ char *ptr;
+ double result = strtod (input, &ptr);
+ ASSERT (result == 0.0);
+ ASSERT (signbit (result) == signbit (-0.0));
+ ASSERT (ptr == input + 2);
+ ASSERT (errno == 0);
+ }
+ {
+ errno = 0;
+ const char input[] = "0xg";
+ char *ptr;
+ double result = strtod (input, &ptr);
+ ASSERT (result == 0.0);
+ ASSERT (!signbit (result));
+ ASSERT (ptr == input + 1);
+ ASSERT (errno == 0);
+ }
+ {
+ errno = 0;
+ const char input[] = "0xp";
+ char *ptr;
+ double result = strtod (input, &ptr);
+ ASSERT (result == 0.0);
+ ASSERT (!signbit (result));
+ ASSERT (ptr == input + 1);
+ ASSERT (errno == 0);
+ }
+ {
+ errno = 0;
+ const char input[] = "0x.";
+ char *ptr;
+ double result = strtod (input, &ptr);
+ ASSERT (result == 0.0);
+ ASSERT (!signbit (result));
+ ASSERT (ptr == input + 1);
+ ASSERT (errno == 0);
+ }
+ {
+ errno = 0;
+ const char input[] = "0xp+";
+ char *ptr;
+ double result = strtod (input, &ptr);
+ ASSERT (result == 0.0);
+ ASSERT (!signbit (result));
+ ASSERT (ptr == input + 1);
+ ASSERT (errno == 0);
+ }
+ {
+ errno = 0;
+ const char input[] = "0xp+1";
+ char *ptr;
+ double result = strtod (input, &ptr);
+ ASSERT (result == 0.0);
+ ASSERT (!signbit (result));
+ ASSERT (ptr == input + 1);
+ ASSERT (errno == 0);
+ }
+ {
+ errno = 0;
+ const char input[] = "0x.p+1";
+ char *ptr;
+ double result = strtod (input, &ptr);
+ ASSERT (result == 0.0);
+ ASSERT (!signbit (result));
+ ASSERT (ptr == input + 1);
+ ASSERT (errno == 0);
+ }
+ {
+ errno = 0;
+ const char input[] = "1p+1";
+ char *ptr;
+ double result = strtod (input, &ptr);
+ ASSERT (result == 1.0);
+ ASSERT (ptr == input + 1);
+ ASSERT (errno == 0);
+ }
+
+ /* Overflow/underflow. */
+ {
+ errno = 0;
+ const char input[] = "1E100000";
+ char *ptr;
+ double result = strtod (input, &ptr);
+ ASSERT (result == HUGE_VAL);
+ ASSERT (ptr == input + 8);
+ ASSERT (errno == ERANGE);
+ }
+ {
+ errno = 0;
+ const char input[] = "-1E100000";
+ char *ptr;
+ double result = strtod (input, &ptr);
+ ASSERT (result == -HUGE_VAL);
+ ASSERT (ptr == input + 9);
+ ASSERT (errno == ERANGE);
+ }
+ {
+ errno = 0;
+ const char input[] = "1E-100000";
+ char *ptr;
+ double result = strtod (input, &ptr);
+ ASSERT (0.0 <= result && result <= FLT_MIN);
+ ASSERT (!signbit (result));
+ ASSERT (ptr == input + 9);
+ ASSERT (errno == ERANGE);
+ }
+ {
+ errno = 0;
+ const char input[] = "-1E-100000";
+ char *ptr;
+ double result = strtod (input, &ptr);
+ ASSERT (-FLT_MIN <= result && result <= 0.0);
+ ASSERT (signbit (result) == signbit (-0.0));
+ ASSERT (ptr == input + 10);
+ ASSERT (errno == ERANGE);
+ }
+
+ /* Infinity. */
+ {
+ errno = 0;
+ const char input[] = "iNf";
+ char *ptr;
+ double result = strtod (input, &ptr);
+ ASSERT (result == HUGE_VAL);
+ ASSERT (ptr == input + 3);
+ ASSERT (errno == 0);
+ }
+ {
+ errno = 0;
+ const char input[] = "-InF";
+ char *ptr;
+ double result = strtod (input, &ptr);
+ ASSERT (result == -HUGE_VAL);
+ ASSERT (ptr == input + 4);
+ ASSERT (errno == 0);
+ }
+ {
+ errno = 0;
+ const char input[] = "infinite";
+ char *ptr;
+ double result = strtod (input, &ptr);
+ ASSERT (result == HUGE_VAL);
+ ASSERT (ptr == input + 3);
+ ASSERT (errno == 0);
+ }
+ {
+ errno = 0;
+ const char input[] = "infinitY";
+ char *ptr;
+ double result = strtod (input, &ptr);
+ ASSERT (result == HUGE_VAL);
+ ASSERT (ptr == input + 8);
+ ASSERT (errno == 0);
+ }
+ {
+ errno = 0;
+ const char input[] = "infinitY.";
+ char *ptr;
+ double result = strtod (input, &ptr);
+ 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. */
+ {
+ errno = 0;
+ const char input[] = "-nan";
+ char *ptr1;
+ char *ptr2;
+ double result1 = strtod (input, &ptr1);
+ double result2 = strtod (input + 1, &ptr2);
+#ifdef NAN
+ ASSERT (isnan (result1));
+ ASSERT (isnan (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
+ }
+ {
+ errno = 0;
+ const char input[] = "+nan(";
+ char *ptr1;
+ char *ptr2;
+ double result1 = strtod (input, &ptr1);
+ double result2 = strtod (input + 1, &ptr2);
+#ifdef NAN
+ ASSERT (isnan (result1));
+ ASSERT (isnan (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
+ }
+ {
+ errno = 0;
+ const char input[] = "-nan()";
+ char *ptr1;
+ char *ptr2;
+ double result1 = strtod (input, &ptr1);
+ double result2 = strtod (input + 1, &ptr2);
+#ifdef NAN
+ ASSERT (isnan (result1));
+ ASSERT (isnan (result2));
+ ASSERT (signbit (result1) != signbit (result2));
+ 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
+ }
+ {
+ errno = 0;
+ const char input[] = " nan().";
+ char *ptr;
+ double result = strtod (input, &ptr);
+#ifdef NAN
+ ASSERT (isnan (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
+ }
+ {
+ errno = 0;
+ /* The behavior of nan(0) is implementation-defined, but all
+ implementations we know of which handle optional
+ n-char-sequences handle nan(0) the same as nan(). */
+ const char input[] = "-nan(0).";
+ char *ptr1;
+ char *ptr2;
+ double result1 = strtod (input, &ptr1);
+ double result2 = strtod (input + 1, &ptr2);
+#ifdef NAN
+ ASSERT (isnan (result1));
+ ASSERT (isnan (result2));
+ ASSERT (signbit (result1) != signbit (result2));
+ 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. */
+#if 0
+ /* TODO - gnulib doesn't implement this yet. */
+ {
+ errno = 0;
+ const char input[] = "0xa";
+ char *ptr;
+ double result = strtod (input, &ptr);
+ ASSERT (result == 10.0);
+ ASSERT (ptr == input + 3);
+ ASSERT (errno == 0);
+ }
+ {
+ errno = 0;
+ const char input[] = "0XA";
+ char *ptr;
+ double result = strtod (input, &ptr);
+ ASSERT (result == 10.0);
+ ASSERT (ptr == input + 3);
+ ASSERT (errno == 0);
+ }
+ {
+ errno = 0;
+ const char input[] = "0x1p";
+ char *ptr;
+ double result = strtod (input, &ptr);
+ ASSERT (result == 1.0);
+ ASSERT (ptr == input + 3);
+ ASSERT (errno == 0);
+ }
+ {
+ errno = 0;
+ const char input[] = "0x1p+";
+ char *ptr;
+ double result = strtod (input, &ptr);
+ ASSERT (result == 1.0);
+ ASSERT (ptr == input + 3);
+ ASSERT (errno == 0);
+ }
+ {
+ errno = 0;
+ const char input[] = "0x1p+1";
+ char *ptr;
+ double result = strtod (input, &ptr);
+ ASSERT (result == 2.0);
+ ASSERT (ptr == input + 6);
+ ASSERT (errno == 0);
+ }
+ {
+ errno = 0;
+ const char input[] = "0x1p+1a";
+ char *ptr;
+ double result = strtod (input, &ptr);
+ ASSERT (result == 2.0);
+ ASSERT (ptr == input + 6);
+ ASSERT (errno == 0);
+ }
+#endif
+
+ /* Large buffers. */
+ {
+ errno = 0;
+ size_t m = 1000000;
+ char *input = malloc (m + 1);
+ if (input)
+ {
+ char *ptr;
+ double result;
+ memset (input, '\t', m - 1);
+ input[m - 1] = '1';
+ input[m] = '\0';
+ result = strtod (input, &ptr);
+ ASSERT (result == 1.0);
+ ASSERT (ptr == input + m);
+ ASSERT (errno == 0);
+ }
+ free (input);
+ }
+ {
+ errno = 0;
+ size_t m = 1000000;
+ char *input = malloc (m + 1);
+ if (input)
+ {
+ char *ptr;
+ double result;
+ memset (input, '0', m - 1);
+ input[m - 1] = '1';
+ input[m] = '\0';
+ result = strtod (input, &ptr);
+ 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. */
+ {
+ errno = 0;
+ size_t m = 1000000;
+ char *input = malloc (m + 1);
+ if (input)
+ {
+ char *ptr;
+ double result;
+ input[0] = '.';
+ memset (input + 1, '0', m - 10);
+ input[m - 9] = '1';
+ input[m - 8] = 'e';
+ input[m - 7] = '+';
+ input[m - 6] = '9';
+ input[m - 5] = '9';
+ input[m - 4] = '9';
+ input[m - 3] = '9';
+ input[m - 2] = '9';
+ input[m - 1] = '1';
+ input[m] = '\0';
+ result = strtod (input, &ptr);
+ ASSERT (result == 1.0);
+ ASSERT (ptr == input + m);
+ ASSERT (errno == 0);
+ }
+ free (input);
+ }
+ {
+ errno = 0;
+ size_t m = 1000000;
+ char *input = malloc (m + 1);
+ if (input)
+ {
+ char *ptr;
+ double result;
+ input[0] = '1';
+ memset (input + 1, '0', m - 9);
+ input[m - 8] = 'e';
+ input[m - 7] = '-';
+ input[m - 6] = '9';
+ input[m - 5] = '9';
+ input[m - 4] = '9';
+ input[m - 3] = '9';
+ input[m - 2] = '9';
+ input[m - 1] = '1';
+ input[m] = '\0';
+ result = strtod (input, &ptr);
+ ASSERT (result == 1.0);
+ ASSERT (ptr == input + m);
+ ASSERT (errno == 0);
+ }
+ free (input);
+ }
+#endif
+ {
+ errno = 0;
+ size_t m = 1000000;
+ char *input = malloc (m + 1);
+ if (input)
+ {
+ char *ptr;
+ double result;
+ input[0] = '-';
+ input[1] = '0';
+ input[2] = 'e';
+ input[3] = '1';
+ memset (input + 4, '0', m - 3);
+ input[m] = '\0';
+ result = strtod (input, &ptr);
+ ASSERT (result == 0.0);
+ ASSERT (signbit (result) == signbit (-0.0));
+ ASSERT (ptr == input + m);
+ ASSERT (errno == 0);
+ }
+ free (input);
+ }
+
+ /* Rounding. */
+ /* TODO - is it worth some tests of rounding for typical IEEE corner
+ cases, such as .5 ULP rounding up to the smallest denormal and
+ not causing underflow, or FLT_MIN - .5 ULP not causing an
+ infinite loop? */
+
+ return status;
+}