summaryrefslogtreecommitdiff
path: root/lib/strtod.c
diff options
context:
space:
mode:
authorBruno Haible <bruno@clisp.org>2019-01-30 03:52:31 +0100
committerBruno Haible <bruno@clisp.org>2019-01-30 03:52:31 +0100
commitebad0cfebbb201a4e645495d244f899a39a305e3 (patch)
tree9ca281e9a8e6a501cb0081d428fb532bd1304018 /lib/strtod.c
parent0babfcddd25cd01492f48bc8b06e3c3b265b7531 (diff)
downloadgnulib-ebad0cfebbb201a4e645495d244f899a39a305e3.tar.gz
strtold: New module.
* lib/stdlib.in.h (strtold): New declaration. * lib/strtold.c: New file. * lib/strtod.c: Consider USE_LONG_DOUBLE. (STRTOD, LDEXP, HAVE_UNDERLYING_STRTOD, DOUBLE, MIN, MAX, L_, USE_LDEXP): New macros. (LDEXP, scale_radix_exp, parse_number, STRTOD): Adapt for USE_LONG_DOUBLE. (underlying_strtod): Remove function. Replace with some macros. Re-add the code for a missing underlying function that was removed on 2013-02-19. * m4/strtold.m4: New file. * m4/stdlib_h.m4 (gl_STDLIB_H): Test whether strtold is declared. (gl_STDLIB_H_DEFAULTS): Initialize GNULIB_STRTOLD, HAVE_STRTOLD, REPLACE_STRTOLD. * modules/stdlib (Makefile.am): Substitute GNULIB_STRTOLD, HAVE_STRTOLD, REPLACE_STRTOLD. * modules/strtold: New file. * doc/posix-functions/strtold.texi: Document the new module.
Diffstat (limited to 'lib/strtod.c')
-rw-r--r--lib/strtod.c113
1 files changed, 73 insertions, 40 deletions
diff --git a/lib/strtod.c b/lib/strtod.c
index 9a28f4bc54..1d1ba2797b 100644
--- a/lib/strtod.c
+++ b/lib/strtod.c
@@ -14,8 +14,11 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. */
-#include <config.h>
+#if ! defined USE_LONG_DOUBLE
+# include <config.h>
+#endif
+/* Specification. */
#include <stdlib.h>
#include <ctype.h>
@@ -28,8 +31,36 @@
#include "c-ctype.h"
-#ifndef HAVE_LDEXP_IN_LIBC
-#define HAVE_LDEXP_IN_LIBC 0
+#undef MIN
+#undef MAX
+#ifdef USE_LONG_DOUBLE
+# define STRTOD strtold
+# define LDEXP ldexpl
+# if defined __hpux && defined __hppa
+ /* We cannot call strtold on HP-UX/hppa, because its return type is a struct,
+ not a 'long double'. */
+# define HAVE_UNDERLYING_STRTOD 0
+# else
+# define HAVE_UNDERLYING_STRTOD HAVE_STRTOLD
+# endif
+# define DOUBLE long double
+# define MIN LDBL_MIN
+# define MAX LDBL_MAX
+# define L_(literal) literal##L
+#else
+# define STRTOD strtod
+# define LDEXP ldexp
+# define HAVE_UNDERLYING_STRTOD 1
+# define DOUBLE double
+# define MIN DBL_MIN
+# define MAX DBL_MAX
+# define L_(literal) literal
+#endif
+
+#if (defined USE_LONG_DOUBLE ? HAVE_LDEXPM_IN_LIBC : HAVE_LDEXP_IN_LIBC)
+# define USE_LDEXP 1
+#else
+# define USE_LDEXP 0
#endif
/* Return true if C is a space in the current locale, avoiding
@@ -41,20 +72,21 @@ locale_isspace (char c)
return isspace (uc) != 0;
}
-#if !HAVE_LDEXP_IN_LIBC
- #define ldexp dummy_ldexp
+#if !USE_LDEXP
+ #undef LDEXP
+ #define LDEXP dummy_ldexp
/* A dummy definition that will never be invoked. */
- static double ldexp (double x _GL_UNUSED, int exponent _GL_UNUSED)
+ static DOUBLE LDEXP (DOUBLE x _GL_UNUSED, int exponent _GL_UNUSED)
{
abort ();
- return 0.0;
+ return L_(0.0);
}
#endif
/* Return X * BASE**EXPONENT. Return an extreme value and set errno
to ERANGE if underflow or overflow occurs. */
-static double
-scale_radix_exp (double x, int radix, long int exponent)
+static DOUBLE
+scale_radix_exp (DOUBLE x, int radix, long int exponent)
{
/* If RADIX == 10, this code is neither precise nor fast; it is
merely a straightforward and relatively portable approximation.
@@ -63,11 +95,11 @@ scale_radix_exp (double x, int radix, long int exponent)
long int e = exponent;
- if (HAVE_LDEXP_IN_LIBC && radix == 2)
- return ldexp (x, e < INT_MIN ? INT_MIN : INT_MAX < e ? INT_MAX : e);
+ if (USE_LDEXP && radix == 2)
+ return LDEXP (x, e < INT_MIN ? INT_MIN : INT_MAX < e ? INT_MAX : e);
else
{
- double r = x;
+ DOUBLE r = x;
if (r != 0)
{
@@ -87,12 +119,12 @@ scale_radix_exp (double x, int radix, long int exponent)
{
while (e-- != 0)
{
- if (r < -DBL_MAX / radix)
+ if (r < -MAX / radix)
{
errno = ERANGE;
return -HUGE_VAL;
}
- else if (DBL_MAX / radix < r)
+ else if (MAX / radix < r)
{
errno = ERANGE;
return HUGE_VAL;
@@ -113,7 +145,7 @@ scale_radix_exp (double x, int radix, long int exponent)
radix RADIX (either 10 or 2) exponent, and exponent character
EXPCHAR. To convert from a number of digits to a radix exponent,
multiply by RADIX_MULTIPLIER (either 1 or 4). */
-static double
+static DOUBLE
parse_number (const char *nptr,
int base, int radix, int radix_multiplier, char expchar,
char **endptr)
@@ -121,7 +153,7 @@ parse_number (const char *nptr,
const char *s = nptr;
bool got_dot = false;
long int exponent = 0;
- double num = 0;
+ DOUBLE num = 0;
for (;; ++s)
{
@@ -141,12 +173,12 @@ parse_number (const char *nptr,
break;
/* Make sure that multiplication by base will not overflow. */
- if (num <= DBL_MAX / base)
+ if (num <= MAX / base)
num = num * base + digit;
else
{
/* The value of the digit doesn't matter, since we have already
- gotten as many digits as can be represented in a 'double'.
+ gotten as many digits as can be represented in a 'DOUBLE'.
This doesn't necessarily mean the result will overflow.
The exponent may reduce it to within range.
@@ -185,32 +217,42 @@ parse_number (const char *nptr,
return scale_radix_exp (num, radix, exponent);
}
-static double underlying_strtod (const char *, char **);
-
/* HP cc on HP-UX 10.20 has a bug with the constant expression -0.0.
ICC 10.0 has a bug when optimizing the expression -zero.
- The expression -DBL_MIN * DBL_MIN does not work when cross-compiling
+ The expression -MIN * MIN does not work when cross-compiling
to PowerPC on Mac OS X 10.5. */
#if defined __hpux || defined __sgi || defined __ICC
-static double
+static DOUBLE
compute_minus_zero (void)
{
- return -DBL_MIN * DBL_MIN;
+ return -MIN * MIN;
}
# define minus_zero compute_minus_zero ()
#else
-double minus_zero = -0.0;
+DOUBLE minus_zero = -0.0;
#endif
-/* Convert NPTR to a double. If ENDPTR is not NULL, a pointer to the
+/* Convert NPTR to a DOUBLE. If ENDPTR is not NULL, a pointer to the
character after the last one used in the number is put in *ENDPTR. */
-double
-strtod (const char *nptr, char **endptr)
+DOUBLE
+STRTOD (const char *nptr, char **endptr)
+#if HAVE_UNDERLYING_STRTOD
+# ifdef USE_LONG_DOUBLE
+# undef strtold
+# else
+# undef strtod
+# endif
+#else
+# undef STRTOD
+# define STRTOD(NPTR,ENDPTR) parse_number (NPTR, 10, 10, 1, 'e', ENDPTR)
+#endif
+/* From here on, STRTOD refers to the underlying implementation. It needs
+ to handle only finite unsigned decimal numbers with non-null ENDPTR. */
{
bool negative = false;
/* The number so far. */
- double num;
+ DOUBLE num;
const char *s = nptr;
const char *end;
@@ -226,7 +268,7 @@ strtod (const char *nptr, char **endptr)
if (*s == '-' || *s == '+')
++s;
- num = underlying_strtod (s, &endbuf);
+ num = STRTOD (s, &endbuf);
end = endbuf;
if (c_isdigit (s[*s == '.']))
@@ -261,7 +303,7 @@ strtod (const char *nptr, char **endptr)
else
{
/* If "1e 1" was misparsed as 10.0 instead of 1.0, re-do the
- underlying strtod on a copy of the original string
+ underlying STRTOD on a copy of the original string
truncated to avoid the bug. */
const char *e = s + 1;
while (e < end && c_tolower (*e) != 'e')
@@ -279,7 +321,7 @@ strtod (const char *nptr, char **endptr)
else
{
dup[e - s] = '\0';
- num = underlying_strtod (dup, &endbuf);
+ num = STRTOD (dup, &endbuf);
saved_errno = errno;
free (dup);
errno = saved_errno;
@@ -344,12 +386,3 @@ strtod (const char *nptr, char **endptr)
return minus_zero;
return negative ? -num : num;
}
-
-/* The underlying strtod implementation. This must be defined
- after strtod because it #undefs strtod. */
-static double
-underlying_strtod (const char *nptr, char **endptr)
-{
-#undef strtod
- return strtod (nptr, endptr);
-}