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
(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>
#include <config.h>
+#include <inttypes.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
++cp;
while (0 <= digit_to_number (*cp, base));
}
-
if (*cp == '.')
{
state |= DOT_CHAR;
: (!*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;
-
- for (cp = string + signedp + 1; ; cp++)
+ uintmax_t n;
+
+ /* 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);
}