2011-05-03 Paul Eggert <eggert@cs.ucla.edu>
+ Arithmetic overflows now return float rather than wrapping around.
+ * data.c: Include <intprops.h>.
+ (arith_driver): Use floating point if the accumulator would otherwise
+ go out of EMACS_INT range.
+ (arith_driver, Fadd1, Fsub1): Use floating point if the result is
+ out of Emacs fixnum range.
+ * bytecode.c (exec_byte_code): Likewise, for Bsub1, Badd1, Bnegate.
+
* callproc.c (Fcall_process): Use 'volatile' to avoid vfork clobbering.
* process.c (Fformat_network_address): Fix typo: args2 -> *args2.
#include <signal.h>
#include <stdio.h>
#include <setjmp.h>
+
+#include <intprops.h>
+
#include "lisp.h"
#include "puresize.h"
#include "character.h"
static Lisp_Object
arith_driver (enum arithop code, size_t nargs, register Lisp_Object *args)
{
- register Lisp_Object val;
register size_t argnum;
register EMACS_INT accum = 0;
- register EMACS_INT next;
switch (SWITCH_ENUM_CAST (code))
{
for (argnum = 0; argnum < nargs; argnum++)
{
+ EMACS_INT a = accum;
+ int use_float = 0;
+
/* Using args[argnum] as argument to CHECK_NUMBER_... */
- val = args[argnum];
+ Lisp_Object val = args[argnum];
CHECK_NUMBER_OR_FLOAT_COERCE_MARKER (val);
+ args[argnum] = val;
if (FLOATP (val))
- return float_arith_driver ((double) accum, argnum, code,
- nargs, args);
- args[argnum] = val;
- next = XINT (args[argnum]);
- switch (SWITCH_ENUM_CAST (code))
+ use_float = 1;
+ else
{
- case Aadd:
- accum += next;
- break;
- case Asub:
- accum = argnum ? accum - next : nargs == 1 ? - next : next;
- break;
- case Amult:
- accum *= next;
- break;
- case Adiv:
- if (!argnum)
- accum = next;
- else
+ EMACS_INT next = XINT (val);
+ switch (SWITCH_ENUM_CAST (code))
{
- if (next == 0)
- xsignal0 (Qarith_error);
- accum /= next;
+ case Aadd:
+ if (next < 0
+ ? a < TYPE_MINIMUM (EMACS_INT) - next
+ : TYPE_MAXIMUM (EMACS_INT) - next < a)
+ use_float = 1;
+ else
+ a += next;
+ break;
+ case Asub:
+ if (argnum == 0 && nargs != 1)
+ a = next;
+ else if (next < 0
+ ? TYPE_MAXIMUM (EMACS_INT) + next < a
+ : a < TYPE_MINIMUM (EMACS_INT) + next)
+ use_float = 1;
+ else
+ a -= next;
+ break;
+ case Amult:
+ if (next < 0
+ ? (a < 0
+ ? a < TYPE_MAXIMUM (EMACS_INT) / next
+ : next != -1 && TYPE_MINIMUM (EMACS_INT) / next < a)
+ : (next != 0
+ && (a < 0
+ ? a < TYPE_MINIMUM (EMACS_INT) / next
+ : TYPE_MAXIMUM (EMACS_INT) / next < a)))
+ use_float = 1;
+ else
+ a *= next;
+ break;
+ case Adiv:
+ if (!argnum)
+ a = next;
+ else
+ {
+ if (next == 0)
+ xsignal0 (Qarith_error);
+ a /= next;
+ }
+ break;
+ case Alogand:
+ a &= next;
+ break;
+ case Alogior:
+ a |= next;
+ break;
+ case Alogxor:
+ a ^= next;
+ break;
+ case Amax:
+ if (!argnum || a < next)
+ a = next;
+ break;
+ case Amin:
+ if (!argnum || next < a)
+ a = next;
+ break;
}
- break;
- case Alogand:
- accum &= next;
- break;
- case Alogior:
- accum |= next;
- break;
- case Alogxor:
- accum ^= next;
- break;
- case Amax:
- if (!argnum || next > accum)
- accum = next;
- break;
- case Amin:
- if (!argnum || next < accum)
- accum = next;
- break;
}
+
+ if (use_float)
+ return float_arith_driver (accum, argnum, code, nargs, args);
+
+ accum = a;
}
- XSETINT (val, accum);
- return val;
+ return make_fixnum_or_float (accum);
}
#undef isnan
if (FLOATP (number))
return (make_float (1.0 + XFLOAT_DATA (number)));
-
+ if (XINT (number) + 1 == MOST_POSITIVE_FIXNUM + 1)
+ return make_float (XINT (number) + 1);
XSETINT (number, XINT (number) + 1);
return number;
}
if (FLOATP (number))
return (make_float (-1.0 + XFLOAT_DATA (number)));
-
+ if (XINT (number) - 1 == MOST_NEGATIVE_FIXNUM - 1)
+ return make_float (XINT (number) - 1);
XSETINT (number, XINT (number) - 1);
return number;
}