aboutsummaryrefslogtreecommitdiff
path: root/stdlib/strtod.c
diff options
context:
space:
mode:
Diffstat (limited to 'stdlib/strtod.c')
-rw-r--r--stdlib/strtod.c149
1 files changed, 91 insertions, 58 deletions
diff --git a/stdlib/strtod.c b/stdlib/strtod.c
index 2f0e972893..8f71d136de 100644
--- a/stdlib/strtod.c
+++ b/stdlib/strtod.c
@@ -102,10 +102,11 @@ static const mp_limb _tens_in_limb[MAX_DIG_PER_LIMB + 1] =
#define RETURN_LIMB_SIZE howmany (MANT_DIG, BITS_PER_MP_LIMB)
#define RETURN(val,end) \
- do { if (endptr != 0) *endptr = (char *) end; return val; } while (0)
+ do { if (endptr != 0) *endptr = (char *) (end); return (val); } while (0)
/* Maximum size necessary for mpn integers to hold floating point numbers. */
-#define MPNSIZE (howmany (MAX_EXP + 2 * MANT_DIG, BITS_PER_MP_LIMB) + 2)
+#define MPNSIZE (howmany (MAX_EXP + 2 * MANT_DIG, BITS_PER_MP_LIMB) \
+ + 2)
/* Declare an mpn integer variable that big. */
#define MPN_VAR(name) mp_limb name[MPNSIZE]; mp_size_t name##size
/* Copy an mpn integer value. */
@@ -276,15 +277,23 @@ __mpn_lshift_1 (mp_limb *ptr, mp_size_t size, unsigned int count, mp_limb limb)
}
+#define INTERNAL(x) INTERNAL1(x)
+#define INTERNAL1(x) __##x##_internal
+
+/* This file defines a function to check for correct grouping. */
+#include "grouping.h"
+
+
/* Return a floating point number with the value of the given string NPTR.
Set *ENDPTR to the character after the last used one. If the number is
smaller than the smallest representable number, set `errno' to ERANGE and
return 0.0. If the number is too big to be represented, set `errno' to
ERANGE and return HUGE_VAL with the approriate sign. */
FLOAT
-STRTOF (nptr, endptr)
- const char *nptr;
- char **endptr;
+INTERNAL (STRTOF) (nptr, endptr, group)
+ const char *nptr;
+ char **endptr;
+ int group;
{
int negative; /* The sign of the number. */
MPN_VAR (num); /* MP representation of the number. */
@@ -301,9 +310,9 @@ STRTOF (nptr, endptr)
int bits;
/* Running pointer after the last character processed in the string. */
- const char *cp;
+ const char *cp, *tp;
/* Start of significant part of the number. */
- const char *startp;
+ const char *startp, *start_of_digits;
/* Points at the character following the integer and fractional digits. */
const char *expp;
/* Total number of digit and number of digits in integer part. */
@@ -313,60 +322,29 @@ STRTOF (nptr, endptr)
/* The radix character of the current locale. */
wchar_t decimal;
-#ifdef USE_GROUPING
/* The thousands character of the current locale. */
wchar_t thousands;
/* The numeric grouping specification of the current locale,
in the format described in <locale.h>. */
const char *grouping;
- /* Check the grouping of the integer part at [BEGIN,END).
- Return zero iff a separator is found out of place. */
- int grouping_ok (const char *begin, const char *end)
+ if (group)
{
- if (grouping)
- while (end > begin)
- {
- const char *p = end;
- do
- --p;
- while (*p != thousands && p > begin);
- if (end - 1 - p != *grouping++)
- return 0; /* Wrong number of digits in this group. */
- end = p; /* Correct group; trim it off the end. */
-
- if (*grouping == 0)
- --grouping; /* Same grouping repeats in next iteration. */
- else if (*grouping == CHAR_MAX || *grouping < 0)
- {
- /* No further grouping allowed. */
- while (end > begin)
- if (*--end == thousands)
- return 0;
- }
- }
- return 1;
- }
- /* Return with no conversion if the grouping of [STARTP,CP) is bad. */
-#define CHECK_GROUPING if (! grouping_ok (startp, cp)) RETURN (0.0, nptr); else
-
- grouping = _NL_CURRENT (LC_NUMERIC, GROUPING);
- if (*grouping <= 0 || *grouping == CHAR_MAX)
- grouping = NULL;
- else
- {
- /* Figure out the thousands seperator character. */
- if (mbtowc (&thousands_sep, _NL_CURRENT (LC_NUMERIC, THOUSANDS_SEP),
- strlen (_NL_CURRENT (LC_NUMERIC, THOUSANDS_SEP))) <= 0)
- thousands = (wchar_t) *_NL_CURRENT (LC_NUMERIC, THOUSANDS_SEP);
- if (thousands == L'\0')
+ grouping = _NL_CURRENT (LC_NUMERIC, GROUPING);
+ if (*grouping <= 0 || *grouping == CHAR_MAX)
grouping = NULL;
+ else
+ {
+ /* Figure out the thousands separator character. */
+ if (mbtowc (&thousands, _NL_CURRENT (LC_NUMERIC, THOUSANDS_SEP),
+ strlen (_NL_CURRENT (LC_NUMERIC, THOUSANDS_SEP))) <= 0)
+ thousands = (wchar_t) *_NL_CURRENT (LC_NUMERIC, THOUSANDS_SEP);
+ if (thousands == L'\0')
+ grouping = NULL;
+ }
}
-#else
-#define grouping NULL
-#define thousands L'\0'
-#define CHECK_GROUPING ((void) 0)
-#endif
+ else
+ grouping = NULL;
/* Find the locale's decimal point character. */
if (mbtowc (&decimal, _NL_CURRENT (LC_NUMERIC, DECIMAL_POINT),
@@ -402,18 +380,21 @@ STRTOF (nptr, endptr)
RETURN (0.0, nptr);
/* Record the start of the digits, in case we will check their grouping. */
- startp = cp;
+ start_of_digits = startp = cp;
/* Ignore leading zeroes. This helps us to avoid useless computations. */
while (c == '0' || (thousands != L'\0' && c == thousands))
c = *++cp;
- CHECK_GROUPING;
-
/* If no other digit but a '0' is found the result is 0.0.
Return current read pointer. */
if (!isdigit (c) && c != decimal)
- RETURN (0.0, cp);
+ {
+ tp = correctly_grouped_prefix (start_of_digits, cp, thousands, grouping);
+ /* If TP is at the start of the digits, there was no correctly
+ grouped prefix of the string; so no number found. */
+ RETURN (0.0, tp == start_of_digits ? nptr : tp);
+ }
/* Remember first significant digit and read following characters until the
decimal point, exponent character or any non-FP number character. */
@@ -432,7 +413,37 @@ STRTOF (nptr, endptr)
c = *++cp;
}
- CHECK_GROUPING;
+ if (grouping && dig_no > 0)
+ {
+ /* Check the grouping of the digits. */
+ tp = correctly_grouped_prefix (start_of_digits, cp, thousands, grouping);
+ if (cp != tp)
+ {
+ /* Less than the entire string was correctly grouped. */
+
+ if (tp == start_of_digits)
+ /* No valid group of numbers at all: no valid number. */
+ RETURN (0.0, nptr);
+
+ if (tp < startp)
+ /* The number is validly grouped, but consists
+ only of zeroes. The whole value is zero. */
+ RETURN (0.0, tp);
+
+ /* Recompute DIG_NO so we won't read more digits than
+ are properly grouped. */
+ cp = tp;
+ dig_no = 0;
+ for (tp = startp; tp < cp; ++tp)
+ if (isdigit (*tp))
+ ++dig_no;
+
+ int_no = dig_no;
+ lead_zero = 0;
+
+ goto number_parsed;
+ }
+ }
if (dig_no >= NDIG)
/* Too many digits to be representable. Assigning this to EXPONENT
@@ -528,6 +539,8 @@ STRTOF (nptr, endptr)
assert (dig_no >= int_no);
}
+ number_parsed:
+
/* The whole string is parsed. Store the address of the next character. */
if (endptr)
*endptr = (char *) cp;
@@ -546,7 +559,7 @@ STRTOF (nptr, endptr)
exponent -= incr;
}
- if (int_no + exponent > MAX_10_EXP)
+ if (int_no + exponent > MAX_10_EXP + 1)
{
errno = ERANGE;
return negative ? -FLOAT_HUGE_VAL : FLOAT_HUGE_VAL;
@@ -607,6 +620,14 @@ STRTOF (nptr, endptr)
count_leading_zeros (bits, num[numsize - 1]);
bits = numsize * BITS_PER_MP_LIMB - bits;
+ /* Now we know the exponent of the number in base two.
+ Check it against the maximum possible exponent. */
+ if (bits > MAX_EXP)
+ {
+ errno = ERANGE;
+ return negative ? -FLOAT_HUGE_VAL : FLOAT_HUGE_VAL;
+ }
+
/* We have already the first BITS bits of the result. Together with
the information whether more non-zero bits follow this is enough
to determine the result. */
@@ -1059,3 +1080,15 @@ STRTOF (nptr, endptr)
/* NOTREACHED */
}
+
+/* External user entry point. */
+
+weak_symbol (STRTOF)
+
+FLOAT
+STRTOF (nptr, endptr)
+ const char *nptr;
+ char **endptr;
+{
+ return INTERNAL (STRTOF) (nptr, endptr, 0);
+}