Skip to content

Commit 0e5f167

Browse files
committed
Merge pull request #853 from Daetalus/test_int
Use adjustment-post division to avoid int division overflow
2 parents bd2ee53 + 58c2726 commit 0e5f167

File tree

2 files changed

+52
-7
lines changed

2 files changed

+52
-7
lines changed

src/runtime/int.cpp

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -320,16 +320,35 @@ extern "C" Box* div_i64_i64(i64 lhs, i64 rhs) {
320320
#if PYSTON_INT_MIN < -PYSTON_INT_MAX
321321
static_assert(PYSTON_INT_MIN == -PYSTON_INT_MAX - 1, "");
322322

323-
if (lhs == PYSTON_INT_MIN) {
324-
return longInt(longDiv(boxLong(lhs), boxLong(rhs)));
323+
if (lhs == PYSTON_INT_MIN && rhs == -1) {
324+
return longDiv(boxLong(lhs), boxLong(rhs));
325325
}
326326
#endif
327327

328-
if (lhs < 0 && rhs > 0)
329-
return boxInt((lhs - rhs + 1) / rhs);
330-
if (lhs > 0 && rhs < 0)
331-
return boxInt((lhs - rhs - 1) / rhs);
332-
return boxInt(lhs / rhs);
328+
i64 div_result, mod_result;
329+
div_result = lhs / rhs;
330+
/* div_result * rhs can overflow on platforms where lhs/rhs gives floor(lhs/rhs)
331+
* for lhs and rhs with differing signs. (This is unusual
332+
* behaviour, and C99 prohibits it, but it's allowed by C89;
333+
* for an example of overflow, take lhs = LONG_MIN, rhs = 5 or lhs =
334+
* LONG_MAX, rhs = -5.) However, lhs - div_result*rhs is always
335+
* representable as a long, since it lies strictly between
336+
* -abs(rhs) and abs(rhs). We add casts to avoid intermediate
337+
* overflow.
338+
*/
339+
mod_result = (i64)(lhs - (unsigned long)div_result * rhs);
340+
/* If the signs of lhs and rhs differ, and the remainder is non-0,
341+
* C89 doesn't define whether div_result is now the floor or the
342+
* ceiling of the infinitely precise quotient. We want the floor,
343+
* and we have it iff the remainder's sign matches rhs's.
344+
*/
345+
if (mod_result && ((rhs ^ mod_result) < 0) /* i.e. and signs differ */) {
346+
mod_result += rhs;
347+
--div_result;
348+
assert(mod_result && ((rhs ^ mod_result) >= 0));
349+
}
350+
351+
return boxInt(div_result);
333352
}
334353

335354
extern "C" i64 mod_i64_i64(i64 lhs, i64 rhs) {

test/tests/intmethods.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,3 +138,29 @@ def call_int(i):
138138
print int(1e100)
139139
print int(*[1e100])
140140
print int(x=1e100)
141+
142+
import sys
143+
min_int = -1 - sys.maxint
144+
max_int = sys.maxint
145+
146+
print(min_int / 2)
147+
print(min_int / -2)
148+
print(min_int / 3)
149+
print(min_int / -3)
150+
print(min_int / 5)
151+
print(min_int / -5)
152+
print(min_int / 7)
153+
print(min_int / -7)
154+
155+
print(max_int / 2)
156+
print(max_int / -2)
157+
print(-max_int / 2)
158+
print(max_int / 3)
159+
print(max_int / -3)
160+
print(-max_int / 3)
161+
print(max_int / 5)
162+
print(max_int / -5)
163+
print(-max_int / 5)
164+
print(max_int / 7)
165+
print(-max_int / 7)
166+
print(max_int / -7)

0 commit comments

Comments
 (0)