1
0
mirror of https://git.savannah.gnu.org/git/emacs.git synced 2024-11-27 07:37:33 +00:00

* lread.c (string_to_number): Use strtoumax, to convert more integers without overflow.

This commit is contained in:
Paul Eggert 2011-04-21 11:57:37 -07:00
parent 452f415013
commit d78050d6ba
2 changed files with 33 additions and 32 deletions

View File

@ -11,7 +11,8 @@
parsing non-base-10 numbers, as the documentation specifies.
* lisp.h (string_to_number): New decl, replacing ...
(isfloat_string): Remove.
* lread.c (read1): Do not accept +. and -. as integers; this
* lread.c: Include <inttypes.h>, for uintmax_t and strtoimax.
(read1): Do not accept +. and -. as integers; this
appears to have been a coding error. Similarly, do not accept
strings like +-1e0 as floating point numbers. Do not report
overflow for integer overflows unless the base is not 10 which
@ -25,7 +26,9 @@
(string_to_number): New function, replacing isfloat_string.
This function checks for valid syntax and produces the resulting
Lisp float number too. Rework it so that string-to-number
no longer mishandles examples like "1.0e+".
no longer mishandles examples like "1.0e+". Use strtoimax,
so that overflow for non-base-10 numbers is reported only when
there's no portable and simple way to convert to floating point.
2011-04-20 Paul Eggert <eggert@cs.ucla.edu>

View File

@ -19,6 +19,7 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
#include <config.h>
#include <inttypes.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
@ -3226,7 +3227,6 @@ string_to_number (char const *string, int base, int ignore_trailing)
++cp;
while (0 <= digit_to_number (*cp, base));
}
if (*cp == '.')
{
state |= DOT_CHAR;
@ -3300,47 +3300,45 @@ string_to_number (char const *string, int base, int ignore_trailing)
: (!*cp && ((state & ~DOT_CHAR) == LEAD_INT || float_syntax))))
return Qnil;
/* If the number does not use float syntax, and fits into a fixnum, return
the fixnum. */
/* If the number uses integer and not float syntax, and is in C-language
range, use its value, preferably as a fixnum. */
if (0 <= leading_digit && ! float_syntax)
{
/* Convert string to EMACS_INT. Do not use strtol, to avoid assuming
that EMACS_INT is no wider than 'long', and because when BASE is 16
strtol might accept numbers like "0x1" that are not allowed here. */
EMACS_INT n = leading_digit;
EMACS_INT abs_bound =
(negative ? -MOST_NEGATIVE_FIXNUM : MOST_POSITIVE_FIXNUM);
EMACS_INT abs_bound_over_base = abs_bound / base;
uintmax_t n;
for (cp = string + signedp + 1; ; cp++)
/* Fast special case for single-digit integers. This also avoids a
glitch when BASE is 16 and IGNORE_TRAILING is nonzero, because in that
case some versions of strtoumax accept numbers like "0x1" that Emacs
does not allow. */
if (digit_to_number (string[signedp + 1], base) < 0)
return make_number (negative ? -leading_digit : leading_digit);
errno = 0;
n = strtoumax (string + signedp, NULL, base);
if (errno == ERANGE)
{
int d = digit_to_number (*cp, base);
if (d < 0)
{
if (n <= abs_bound)
return make_number (negative ? -n : n);
break;
}
if (abs_bound_over_base < n)
break;
n = base * n + d;
/* Unfortunately there's no simple and accurate way to convert
non-base-10 numbers that are out of C-language range. */
if (base != 10)
xsignal (Qoverflow_error, list1 (build_string (string)));
}
/* Unfortunately there's no simple and reliable way to convert
non-base-10 to floating point. */
if (base != 10)
xsignal (Qoverflow_error, list1 (build_string (string)));
else if (n <= (negative ? -MOST_NEGATIVE_FIXNUM : MOST_POSITIVE_FIXNUM))
{
EMACS_INT signed_n = n;
return make_number (negative ? -signed_n : signed_n);
}
else
value = n;
}
/* Either the number uses float syntax, or it does not fit into a fixnum.
Convert it from string to floating point, unless the value is already
known because it is an infinity or a NAN. */
known because it is an infinity, a NAN, or its absolute value fits in
uintmax_t. */
if (! value)
value = atof (string + signedp);
if (negative)
value = -value;
return make_float (value);
return make_float (negative ? -value : value);
}