Skip to content

Commit b1373ea

Browse files
committed
gh-156443: Keep PyLong loop carries as twodigits in shifts and division
1 parent 5056ac5 commit b1373ea

2 files changed

Lines changed: 60 additions & 12 deletions

File tree

Lib/test/test_long.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,56 @@ def test_division(self):
204204
self.check_division(710031681576388032, 26769404391308)
205205
self.check_division(1933622614268221, 30212853348836)
206206

207+
def test_divmod_full_limbs(self):
208+
# Use saturated limbs and q near BASE to maximize |z| (~BASE*q)
209+
# in the x_divrem inner loop: z = vk[i] + zhi - q*w0[i].
210+
for n_div in (2, 3, 5, 8):
211+
# n_div MASK limbs for the divisor (w0)
212+
w = (1 << (n_div * SHIFT)) - 1
213+
for n_num in (n_div, n_div + 1, n_div + 4):
214+
# n_num MASK limbs for the dividend (vk)
215+
v = (1 << (n_num * SHIFT)) - 1
216+
with self.subTest(n_div=n_div, n_num=n_num):
217+
self.check_division(v, w)
218+
219+
# Known quotient and remainder: q includes values
220+
# near BASE and r spans 0 .. w-1.
221+
for q in (1, 2, MASK, BASE - 1, BASE, BASE + 1):
222+
for r in (0, 1, MASK, w - 1):
223+
with self.subTest(n_div=n_div, q=q, r=r):
224+
v = q * w + r
225+
self.assertEqual(divmod(v, w), (q, r))
226+
self.check_division(v, w)
207227

228+
@support.requires_IEEE_754
229+
def test_intradigit_shift(self):
230+
# Unit tests for v_lshift and v_rshift in longobject.c.
231+
# These two functions are not used by Python << and >>,
232+
# so it is different with tests of test_xxx_l|rshift.
233+
# We test them with other functions that use them.
234+
235+
# Full limb values.
236+
one = (1 << SHIFT) - 1
237+
two = (1 << (2 * SHIFT)) - 1
238+
three = (1 << (3 * SHIFT)) - 1
239+
four = (1 << (4 * SHIFT)) - 1
240+
# Powers of 10.
241+
ten_to_40 = 10**40
242+
ten_to_20 = 10**20
243+
244+
# Test with "_PyLong_Frexp" (n -> float):
245+
# - n.bit_length() <= 55 => v_lshift,
246+
# - n.bit_length() > 55 => v_rshift.
247+
self.check_float_conversion(one)
248+
self.check_float_conversion(two)
249+
self.check_float_conversion(ten_to_40)
250+
251+
# Test with "long_true_divide" (a / b):
252+
# - (a.bit_length() - b.bit_length()) <= 55 => v_lshift,
253+
# - (a.bit_length() - b.bit_length()) > 55 => v_rshift.
254+
self.check_truediv(three, two)
255+
self.check_truediv(four, two)
256+
self.check_truediv(ten_to_40, ten_to_20)
208257

209258
def test_karatsuba(self):
210259
digits = list(range(1, 5)) + list(range(KARATSUBA_CUTOFF,

Objects/longobject.c

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1905,15 +1905,15 @@ static digit
19051905
v_lshift(digit *z, digit *a, Py_ssize_t m, int d)
19061906
{
19071907
Py_ssize_t i;
1908-
digit carry = 0;
1908+
twodigits carry = 0;
19091909

19101910
assert(0 <= d && d < PyLong_SHIFT);
19111911
for (i=0; i < m; i++) {
19121912
twodigits acc = (twodigits)a[i] << d | carry;
19131913
z[i] = (digit)acc & PyLong_MASK;
1914-
carry = (digit)(acc >> PyLong_SHIFT);
1914+
carry = acc >> PyLong_SHIFT;
19151915
}
1916-
return carry;
1916+
return (digit)carry;
19171917
}
19181918

19191919
/* Shift digit vector a[0:m] d bits right, with 0 <= d < PyLong_SHIFT. Put
@@ -1923,16 +1923,16 @@ static digit
19231923
v_rshift(digit *z, digit *a, Py_ssize_t m, int d)
19241924
{
19251925
Py_ssize_t i;
1926-
digit carry = 0;
1927-
digit mask = ((digit)1 << d) - 1U;
1926+
twodigits carry = 0;
1927+
twodigits mask = ((twodigits)1 << d) - 1U;
19281928

19291929
assert(0 <= d && d < PyLong_SHIFT);
19301930
for (i=m; i-- > 0;) {
1931-
twodigits acc = (twodigits)carry << PyLong_SHIFT | a[i];
1932-
carry = (digit)acc & mask;
1931+
twodigits acc = carry << PyLong_SHIFT | a[i];
1932+
carry = acc & mask;
19331933
z[i] = (digit)(acc >> d);
19341934
}
1935-
return carry;
1935+
return (digit)carry;
19361936
}
19371937

19381938
/* Divide long pin, w/ size digits, by non-zero digit n, storing quotient
@@ -3325,7 +3325,7 @@ x_divrem(PyLongObject *v1, PyLongObject *w1, PyLongObject **prem)
33253325
int d;
33263326
digit wm1, wm2, carry, q, r, vtop, *v0, *vk, *w0, *ak;
33273327
twodigits vv;
3328-
sdigit zhi;
3328+
stwodigits zhi;
33293329
stwodigits z;
33303330

33313331
/* We follow Knuth [The Art of Computer Programming, Vol. 2 (3rd
@@ -3416,11 +3416,10 @@ x_divrem(PyLongObject *v1, PyLongObject *w1, PyLongObject **prem)
34163416
for (i = 0; i < size_w; ++i) {
34173417
/* invariants: -PyLong_BASE <= -q <= zhi <= 0;
34183418
-PyLong_BASE * q <= z < PyLong_BASE */
3419-
z = (sdigit)vk[i] + zhi -
3419+
z = (stwodigits)(sdigit)vk[i] + zhi -
34203420
(stwodigits)q * (stwodigits)w0[i];
34213421
vk[i] = (digit)z & PyLong_MASK;
3422-
zhi = (sdigit)Py_ARITHMETIC_RIGHT_SHIFT(stwodigits,
3423-
z, PyLong_SHIFT);
3422+
zhi = Py_ARITHMETIC_RIGHT_SHIFT(stwodigits, z, PyLong_SHIFT);
34243423
}
34253424

34263425
/* add w back if q was too large (this branch taken rarely) */

0 commit comments

Comments
 (0)