2020-06-06
2020-06-06 11:58:11 by Marcus Comstedt <marcus@mc.pp.se>
-
a831312747c7838492933c2e3fb7b37f2f6d7930
(171 lines)
(+73/-98)
[
Show
| Annotate
]
Branch: master
Compiler: Always use system strto(l)d to convert floats
This guarantees identical results regardless of size shift, and also
gives better precision than our own implementation.
2608:
character after the last one used in the number is put in *ENDPTR. */
PMOD_EXPORT double STRTOD_PCHARP(const PCHARP nptr, PCHARP *endptr)
{
- /* Note: Code duplication in strtod. */
+ /* Note: Code duplication in STRTOLD_PCHARP. */
- PCHARP s;
+ PCHARP s, sdigits, sdigitsend;
short int sign;
- /* The number so far. */
+ /* The number. */
double num;
- int got_dot; /* Found a decimal point. */
- int got_digit; /* Seen any digits. */
- int precision_loss;/* No more digits fit in mantissa */
+ int got_dot; /* Found a decimal point. */
+ size_t got_digits; /* Number of digits seen. */
/* The exponent of the number. */
long int exponent;
-
+ /* Narrow string buffer */
+ char *buf, *p;
+ size_t buf_len;
+ int free_buf;
+
if (nptr.ptr == NULL)
{
errno = EINVAL;
2639:
if (EXTRACT_PCHARP(s) == '-' || EXTRACT_PCHARP(s) == '+')
INC_PCHARP(s,1);
- num = 0.0;
+
got_dot = 0;
- got_digit = 0;
+ got_digits = 0;
exponent = 0;
- precision_loss = 0;
+ sdigits = s;
for (;; INC_PCHARP(s,1))
{
if (WIDE_ISDIGIT (EXTRACT_PCHARP(s)))
{
- got_digit = 1;
+ got_digits ++;
- /* Make sure that multiplication by 10 will not overflow. */
- if (precision_loss || num > DBL_MAX * 0.1)
- /* The value of the digit doesn't matter, since we have already
- 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.
-
- We just need to record that there was another
- digit so that we can multiply by 10 later. */
- ++exponent;
- else {
- int v = EXTRACT_PCHARP(s) - '0';
- num *= 10.0;
- if (v != 0) {
- double check = num;
- num += v;
- if (num == check)
- precision_loss = 1;
- }
- }
-
+
/* Keep track of the number of digits after the decimal point.
If we just divided by 10 here, we would lose precision. */
if (got_dot)
2684:
break;
}
- if (!got_digit)
+ if (!got_digits)
goto noconv;
-
+ sdigitsend = s;
+
if (EXTRACT_PCHARP(s) == 'E' || EXTRACT_PCHARP(s) == 'e')
{
/* Get the exponent specified after the `e' or `E'. */
2729:
if (endptr != NULL)
*endptr = s;
- if (num == 0.0)
- return 0.0;
+ /* Allocate enough for all digits, the exponent, the exponent sign,
+ the letter 'E' and the trailing NUL */
+ buf_len = got_digits + (3 + (sizeof(exponent) * CHAR_BIT + 2) / 3);
- /* Multiply NUM by 10 to the EXPONENT power,
- checking for overflow and underflow. */
+ if (buf_len > 100) {
+ buf = xalloc(buf_len);
+ free_buf = 1;
+ } else {
+ buf = alloca(buf_len);
+ free_buf = 0;
+ }
- if (exponent < 0)
+ p = buf;
+ for (; COMPARE_PCHARP(sdigits,<,sdigitsend); INC_PCHARP(sdigits,1))
{
- if (num < DBL_MIN * pow(10.0, (double) -exponent))
- goto underflow;
+ char ch = EXTRACT_PCHARP(sdigits);
+ if (ch != '.')
+ *p++ = ch;
}
- else if (exponent > 0)
- {
- if (num > DBL_MAX * pow(10.0, (double) -exponent))
- goto overflow;
- }
+ sprintf(p, "E%ld", exponent);
- if(exponent < 0 && exponent >-100) /* make sure we don't underflow */
- num /= pow(10.0, (double) -exponent);
- else
- num *= pow(10.0, (double) exponent);
+ num = strtod(buf, NULL);
+ if (free_buf)
+ free (buf);
return num * sign;
2775: Inside #if SIZEOF_FLOAT_TYPE > SIZEOF_DOUBLE
character after the last one used in the number is put in *ENDPTR. */
PMOD_EXPORT long double STRTOLD_PCHARP(const PCHARP nptr, PCHARP *endptr)
{
- /* Note: Code duplication in strtod. */
+ /* Note: Code duplication in STRTOD_PCHARP. */
- PCHARP s;
+ PCHARP s, sdigits, sdigitsend;
short int sign;
- /* The number so far. */
+ /* The number. */
long double num;
- int got_dot; /* Found a decimal point. */
- int got_digit; /* Seen any digits. */
- int precision_loss;/* No more digits fit in mantissa */
+ int got_dot; /* Found a decimal point. */
+ size_t got_digits; /* Number of digits seen. */
/* The exponent of the number. */
long int exponent;
-
+ /* Narrow string buffer */
+ char *buf, *p;
+ size_t buf_len;
+ int free_buf;
+
if (nptr.ptr == NULL)
{
errno = EINVAL;
2806: Inside #if SIZEOF_FLOAT_TYPE > SIZEOF_DOUBLE
if (EXTRACT_PCHARP(s) == '-' || EXTRACT_PCHARP(s) == '+')
INC_PCHARP(s,1);
- num = 0.0;
+
got_dot = 0;
- got_digit = 0;
+ got_digits = 0;
exponent = 0;
- precision_loss = 0;
+ sdigits = s;
for (;; INC_PCHARP(s,1))
{
if (WIDE_ISDIGIT (EXTRACT_PCHARP(s)))
{
- got_digit = 1;
+ got_digits ++;
- /* Make sure that multiplication by 10 will not overflow. */
- if (precision_loss || num > LDBL_MAX * 0.1)
- /* The value of the digit doesn't matter, since we have already
- gotten as many digits as can be represented in a `long double'.
- This doesn't necessarily mean the result will overflow.
- The exponent may reduce it to within range.
-
- We just need to record that there was another
- digit so that we can multiply by 10 later. */
- ++exponent;
- else {
- int v = EXTRACT_PCHARP(s) - '0';
- num *= 10.0;
- if (v != 0) {
- long double check = num;
- num += v;
- if (num == check)
- precision_loss = 1;
- }
- }
-
+
/* Keep track of the number of digits after the decimal point.
If we just divided by 10 here, we would lose precision. */
if (got_dot)
2851: Inside #if SIZEOF_FLOAT_TYPE > SIZEOF_DOUBLE
break;
}
- if (!got_digit)
+ if (!got_digits)
goto noconv;
-
+ sdigitsend = s;
+
if (EXTRACT_PCHARP(s) == 'E' || EXTRACT_PCHARP(s) == 'e')
{
/* Get the exponent specified after the `e' or `E'. */
2896: Inside #if SIZEOF_FLOAT_TYPE > SIZEOF_DOUBLE
if (endptr != NULL)
*endptr = s;
- if (num == 0.0)
- return 0.0;
+ /* Allocate enough for all digits, the exponent, the exponent sign,
+ the letter 'E' and the trailing NUL */
+ buf_len = got_digits + (3 + (sizeof(exponent) * CHAR_BIT + 2) / 3);
- /* Multiply NUM by 10 to the EXPONENT power,
- checking for overflow and underflow. */
+ if (buf_len > 100) {
+ buf = xalloc(buf_len);
+ free_buf = 1;
+ } else {
+ buf = alloca(buf_len);
+ free_buf = 0;
+ }
- if (exponent < 0)
+ p = buf;
+ for (; COMPARE_PCHARP(sdigits,<,sdigitsend); INC_PCHARP(sdigits,1))
{
- if (num < LDBL_MIN * pow(10.0, (double) -exponent))
- goto underflow;
+ char ch = EXTRACT_PCHARP(sdigits);
+ if (ch != '.')
+ *p++ = ch;
}
- else if (exponent > 0)
- {
- if (num > LDBL_MAX * pow(10.0, (double) -exponent))
- goto overflow;
- }
+ sprintf(p, "E%ld", exponent);
- if(exponent < 0 && exponent >-100) /* make sure we don't underflow */
- num /= powl(10.0, (long double) -exponent);
- else
- num *= powl(10.0, (long double) exponent);
+ num = strtold(buf, NULL);
+ if (free_buf)
+ free (buf);
return num * sign;