Skip to content
Open
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions Lib/test/test_long.py
Original file line number Diff line number Diff line change
Expand Up @@ -1639,6 +1639,13 @@ class MyInt(int):
MyInt.__basicsize__ + MyInt.__itemsize__ * ndigits
)

def test_long_add_overallocate(self):
# see gh-100688
x = (MASK//2) * (MASK+1)
x2 = (MASK//2 + 1) * (MASK+1)
z = x + x2
self.assertEqual(x + x2, MASK * (MASK + 1))


if __name__ == "__main__":
unittest.main()
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Reduce frequency of overallocation in some cases of multidigit integer
additions and subtractions. Thanks Pieter Eendebak for contributions.
19 changes: 17 additions & 2 deletions Objects/longobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -3356,14 +3356,26 @@ x_add(PyLongObject *a, PyLongObject *b)
Py_ssize_t i;
digit carry = 0;

/* There are fast paths for cases where a and b both have at most a single
digit, so if we end up here then at least one of them is multi-digit. */
assert(size_a >= 2 || size_b >= 2);

/* Ensure a is the larger of the two: */
if (size_a < size_b) {
{ PyLongObject *temp = a; a = b; b = temp; }
{ Py_ssize_t size_temp = size_a;
size_a = size_b;
size_b = size_temp; }
}
z = _PyLong_New(size_a+1);

/* Allocate sufficient space for the result. In the majority of cases
we allocate exactly the right number of digits, but in the (relatively
rare) case where the sum of the topmost digits is exactly PyLong_MASK,
we'll sometimes end up overallocating by a single digit. */
digit top_sum = a->long_value.ob_digit[size_a - 1]
+ (size_b == size_a ? b->long_value.ob_digit[size_b - 1] : (digit)0);
int extra_digit = top_sum >= PyLong_MASK;
z = _PyLong_New(size_a + extra_digit);
if (z == NULL)
return NULL;
for (i = 0; i < size_b; ++i) {
Expand All @@ -3376,7 +3388,10 @@ x_add(PyLongObject *a, PyLongObject *b)
z->long_value.ob_digit[i] = carry & PyLong_MASK;
carry >>= PyLong_SHIFT;
}
z->long_value.ob_digit[i] = carry;
assert(carry == 0 || extra_digit);
if (extra_digit) {
z->long_value.ob_digit[i] = carry;
}
return long_normalize(z);
}

Expand Down