Fix for possible signed overflow: the behaviour of -LONG_MIN is

undefined in ANSI C.
This commit is contained in:
Mark Dickinson 2008-04-15 20:51:18 +00:00
parent 8c65b1ed78
commit 27a632510e
1 changed files with 19 additions and 6 deletions

View File

@ -99,20 +99,27 @@ PyObject *
PyLong_FromLong(long ival) PyLong_FromLong(long ival)
{ {
PyLongObject *v; PyLongObject *v;
unsigned long abs_ival;
unsigned long t; /* unsigned so >> doesn't propagate sign bit */ unsigned long t; /* unsigned so >> doesn't propagate sign bit */
int ndigits = 0; int ndigits = 0;
int negative = 0; int negative = 0;
if (ival < 0) { if (ival < 0) {
ival = -ival; /* if LONG_MIN == -LONG_MAX-1 (true on most platforms) then
ANSI C says that the result of -ival is undefined when ival
== LONG_MIN. Hence the following workaround. */
abs_ival = (unsigned long)(-1-ival) + 1;
negative = 1; negative = 1;
} }
else {
abs_ival = (unsigned long)ival;
}
/* Count the number of Python digits. /* Count the number of Python digits.
We used to pick 5 ("big enough for anything"), but that's a We used to pick 5 ("big enough for anything"), but that's a
waste of time and space given that 5*15 = 75 bits are rarely waste of time and space given that 5*15 = 75 bits are rarely
needed. */ needed. */
t = (unsigned long)ival; t = abs_ival;
while (t) { while (t) {
++ndigits; ++ndigits;
t >>= PyLong_SHIFT; t >>= PyLong_SHIFT;
@ -121,7 +128,7 @@ PyLong_FromLong(long ival)
if (v != NULL) { if (v != NULL) {
digit *p = v->ob_digit; digit *p = v->ob_digit;
v->ob_size = negative ? -ndigits : ndigits; v->ob_size = negative ? -ndigits : ndigits;
t = (unsigned long)ival; t = abs_ival;
while (t) { while (t) {
*p++ = (digit)(t & PyLong_MASK); *p++ = (digit)(t & PyLong_MASK);
t >>= PyLong_SHIFT; t >>= PyLong_SHIFT;
@ -830,20 +837,26 @@ PyObject *
PyLong_FromLongLong(PY_LONG_LONG ival) PyLong_FromLongLong(PY_LONG_LONG ival)
{ {
PyLongObject *v; PyLongObject *v;
unsigned PY_LONG_LONG abs_ival;
unsigned PY_LONG_LONG t; /* unsigned so >> doesn't propagate sign bit */ unsigned PY_LONG_LONG t; /* unsigned so >> doesn't propagate sign bit */
int ndigits = 0; int ndigits = 0;
int negative = 0; int negative = 0;
if (ival < 0) { if (ival < 0) {
ival = -ival; /* avoid signed overflow on negation; see comments
in PyLong_FromLong above. */
abs_ival = (unsigned PY_LONG_LONG)(-1-ival) + 1;
negative = 1; negative = 1;
} }
else {
abs_ival = (unsigned PY_LONG_LONG)ival;
}
/* Count the number of Python digits. /* Count the number of Python digits.
We used to pick 5 ("big enough for anything"), but that's a We used to pick 5 ("big enough for anything"), but that's a
waste of time and space given that 5*15 = 75 bits are rarely waste of time and space given that 5*15 = 75 bits are rarely
needed. */ needed. */
t = (unsigned PY_LONG_LONG)ival; t = abs_ival;
while (t) { while (t) {
++ndigits; ++ndigits;
t >>= PyLong_SHIFT; t >>= PyLong_SHIFT;
@ -852,7 +865,7 @@ PyLong_FromLongLong(PY_LONG_LONG ival)
if (v != NULL) { if (v != NULL) {
digit *p = v->ob_digit; digit *p = v->ob_digit;
Py_SIZE(v) = negative ? -ndigits : ndigits; Py_SIZE(v) = negative ? -ndigits : ndigits;
t = (unsigned PY_LONG_LONG)ival; t = abs_ival;
while (t) { while (t) {
*p++ = (digit)(t & PyLong_MASK); *p++ = (digit)(t & PyLong_MASK);
t >>= PyLong_SHIFT; t >>= PyLong_SHIFT;