diff --git a/Lib/test/test_long.py b/Lib/test/test_long.py index 7c368e121024d27..c16bf1b1fe020e9 100644 --- a/Lib/test/test_long.py +++ b/Lib/test/test_long.py @@ -204,7 +204,33 @@ def test_division(self): self.check_division(710031681576388032, 26769404391308) self.check_division(1933622614268221, 30212853348836) - + @support.requires_IEEE_754 + def test_intradigit_shift(self): + # Unit tests for v_lshift and v_rshift in longobject.c. + # These two functions are not used by Python << and >>, + # exercise them via float conversion and true division. + + # Full limb values. + two_limbs = (1 << (2 * SHIFT)) - 1 + three_limbs = (1 << (3 * SHIFT)) - 1 + four_limbs = (1 << (4 * SHIFT)) - 1 + # Multi-limb (not compact) but bit_length() still <= 55. + bits50 = (1 << 50) - 1 + + # _PyLong_Frexp (float(n)): + # v_lshift if n.bit_length() <= 55, else v_rshift. + self.check_float_conversion(bits50) + self.check_float_conversion(two_limbs) + self.check_float_conversion(four_limbs) + self.check_float_conversion(10**40) + + # long_true_divide (a / b): + # - (a.bit_length() - b.bit_length()) <= 55 => v_lshift, + # - (a.bit_length() - b.bit_length()) > 55 => v_rshift. + self.check_truediv(three_limbs, two_limbs) + self.check_truediv(four_limbs, two_limbs) + self.check_truediv(four_limbs, three_limbs) + self.check_truediv(10**40, 10**20) def test_karatsuba(self): digits = list(range(1, 5)) + list(range(KARATSUBA_CUTOFF, diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-09-07-09-52-21.gh-issue-156443.tT40sm.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-09-07-09-52-21.gh-issue-156443.tT40sm.rst new file mode 100644 index 000000000000000..aa60f0b0f7036ad --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-09-07-09-52-21.gh-issue-156443.tT40sm.rst @@ -0,0 +1 @@ +Optimize shifts of large :class:`int` values. diff --git a/Objects/longobject.c b/Objects/longobject.c index f5526213944011c..1a2a8d7242702e6 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -1905,15 +1905,15 @@ static digit v_lshift(digit *z, digit *a, Py_ssize_t m, int d) { Py_ssize_t i; - digit carry = 0; + twodigits carry = 0; assert(0 <= d && d < PyLong_SHIFT); for (i=0; i < m; i++) { twodigits acc = (twodigits)a[i] << d | carry; z[i] = (digit)acc & PyLong_MASK; - carry = (digit)(acc >> PyLong_SHIFT); + carry = acc >> PyLong_SHIFT; } - return carry; + return (digit)carry; } /* Shift digit vector a[0:m] d bits right, with 0 <= d < PyLong_SHIFT. Put @@ -1923,16 +1923,16 @@ static digit v_rshift(digit *z, digit *a, Py_ssize_t m, int d) { Py_ssize_t i; - digit carry = 0; - digit mask = ((digit)1 << d) - 1U; + twodigits carry = 0; + twodigits mask = ((twodigits)1 << d) - 1U; assert(0 <= d && d < PyLong_SHIFT); for (i=m; i-- > 0;) { - twodigits acc = (twodigits)carry << PyLong_SHIFT | a[i]; - carry = (digit)acc & mask; + twodigits acc = carry << PyLong_SHIFT | a[i]; + carry = acc & mask; z[i] = (digit)(acc >> d); } - return carry; + return (digit)carry; } /* Divide long pin, w/ size digits, by non-zero digit n, storing quotient