]> git.eshelyaron.com Git - emacs.git/commitdiff
* data.c: Avoid integer truncation in expressions involving floats.
authorPaul Eggert <eggert@cs.ucla.edu>
Sat, 21 May 2011 05:38:43 +0000 (22:38 -0700)
committerPaul Eggert <eggert@cs.ucla.edu>
Sat, 21 May 2011 05:38:43 +0000 (22:38 -0700)
* data.c: Include <intprops.h>.
(arith_driver): When there's an integer overflow in an expression
involving floating point, convert the integers to floating point
so that the resulting value does not suffer from catastrophic
integer truncation.  For example, on a 64-bit host (* 4
most-negative-fixnum 0.5) should yield about -4.6e+18, not zero.
Do not rely on undefined behavior after integer overflow.

src/ChangeLog
src/data.c

index 8bd4426781d7ef34aaa6908835de9ed24c4fb220..5518531bea8369222dfe120e85ad51fb5bfd5f26 100644 (file)
@@ -1,3 +1,14 @@
+2011-05-21  Paul Eggert  <eggert@cs.ucla.edu>
+
+       * data.c: Avoid integer truncation in expressions involving floats.
+       * data.c: Include <intprops.h>.
+       (arith_driver): When there's an integer overflow in an expression
+       involving floating point, convert the integers to floating point
+       so that the resulting value does not suffer from catastrophic
+       integer truncation.  For example, on a 64-bit host (* 4
+       most-negative-fixnum 0.5) should yield about -4.6e+18, not zero.
+       Do not rely on undefined behavior after integer overflow.
+
 2011-05-20  Paul Eggert  <eggert@cs.ucla.edu>
 
        merge count_size_as_multibyte, parse_str_to_multibyte
index 577ae777d893f2c4ecd741b6b670f2cd58d5944d..073fb0d492604dfac211ac3c0fe800222052014b 100644 (file)
@@ -22,6 +22,9 @@ along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
 #include <signal.h>
 #include <stdio.h>
 #include <setjmp.h>
+
+#include <intprops.h>
+
 #include "lisp.h"
 #include "puresize.h"
 #include "character.h"
@@ -2431,6 +2434,10 @@ arith_driver (enum arithop code, size_t nargs, register Lisp_Object *args)
   register EMACS_INT accum = 0;
   register EMACS_INT next;
 
+  int overflow = 0;
+  size_t ok_args;
+  EMACS_INT ok_accum;
+
   switch (SWITCH_ENUM_CAST (code))
     {
     case Alogior:
@@ -2451,25 +2458,47 @@ arith_driver (enum arithop code, size_t nargs, register Lisp_Object *args)
 
   for (argnum = 0; argnum < nargs; argnum++)
     {
+      if (! overflow)
+       {
+         ok_args = argnum;
+         ok_accum = accum;
+       }
+
       /* Using args[argnum] as argument to CHECK_NUMBER_... */
       val = args[argnum];
       CHECK_NUMBER_OR_FLOAT_COERCE_MARKER (val);
 
       if (FLOATP (val))
-       return float_arith_driver ((double) accum, argnum, code,
+       return float_arith_driver (ok_accum, ok_args, code,
                                   nargs, args);
       args[argnum] = val;
       next = XINT (args[argnum]);
       switch (SWITCH_ENUM_CAST (code))
        {
        case Aadd:
+         if (INT_ADD_OVERFLOW (accum, next))
+           {
+             overflow = 1;
+             accum &= INTMASK;
+           }
          accum += next;
          break;
        case Asub:
+         if (INT_SUBTRACT_OVERFLOW (accum, next))
+           {
+             overflow = 1;
+             accum &= INTMASK;
+           }
          accum = argnum ? accum - next : nargs == 1 ? - next : next;
          break;
        case Amult:
-         accum *= next;
+         if (INT_MULTIPLY_OVERFLOW (accum, next))
+           {
+             overflow = 1;
+             accum = (EMACS_UINT) accum * (EMACS_UINT) next & INTMASK;
+           }
+         else
+           accum *= next;
          break;
        case Adiv:
          if (!argnum)
@@ -2501,6 +2530,9 @@ arith_driver (enum arithop code, size_t nargs, register Lisp_Object *args)
        }
     }
 
+  accum &= INTMASK;
+  if (MOST_POSITIVE_FIXNUM < accum)
+    accum += MOST_NEGATIVE_FIXNUM - (MOST_POSITIVE_FIXNUM + 1);
   XSETINT (val, accum);
   return val;
 }