]> code.delx.au - gnu-emacs/commitdiff
* lread.c (string_to_number): Use strtoumax, to convert more integers without overflow.
authorPaul Eggert <eggert@cs.ucla.edu>
Thu, 21 Apr 2011 18:57:37 +0000 (11:57 -0700)
committerPaul Eggert <eggert@cs.ucla.edu>
Thu, 21 Apr 2011 18:57:37 +0000 (11:57 -0700)
src/ChangeLog
src/lread.c

index 2b9978f3d6a67fddef9dc7d3d9c0ca463d02f55e..0ea90a4d8e5b7dafb1051df272c4d48a66046769 100644 (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>
 
index 390c57d16782da829b1e4bae1740b3d90b17e676..531fee3fa3bf77d75d8304ece8f4d6355f691f80 100644 (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;
-
-      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);
 }