diff options
author | Bruno Haible <bruno@clisp.org> | 2019-01-30 03:52:31 +0100 |
---|---|---|
committer | Bruno Haible <bruno@clisp.org> | 2019-01-30 03:52:31 +0100 |
commit | ebad0cfebbb201a4e645495d244f899a39a305e3 (patch) | |
tree | 9ca281e9a8e6a501cb0081d428fb532bd1304018 /lib/strtod.c | |
parent | 0babfcddd25cd01492f48bc8b06e3c3b265b7531 (diff) | |
download | gnulib-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.c | 113 |
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); -} |