aboutsummaryrefslogtreecommitdiffstats
path: root/src/data.c
diff options
context:
space:
mode:
authorPaul Eggert <[email protected]>2011-05-20 22:38:43 -0700
committerPaul Eggert <[email protected]>2011-05-20 22:38:43 -0700
commit0ae6bdee0085b05028108325b0a4ce979eadb24e (patch)
tree51746679d14a133e355aeb2effe115db5dd692f5 /src/data.c
parentde883a701d8f0db9595c6c459fdff9e3bb20bc83 (diff)
* 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.
Diffstat (limited to 'src/data.c')
-rw-r--r--src/data.c36
1 files changed, 34 insertions, 2 deletions
diff --git a/src/data.c b/src/data.c
index 577ae777d8..073fb0d492 100644
--- a/src/data.c
+++ b/src/data.c
@@ -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;
}