https://github.com/python/cpython/commit/0d3ab6f17e1119095ce9c452cd14c15e60256b50
commit: 0d3ab6f17e1119095ce9c452cd14c15e60256b50
branch: main
author: XiaohongGong <[email protected]>
committer: eendebakpt <[email protected]>
date: 2026-11-04T16:23:23+01:00
summary:

gh-156443: Keep PyLong loop carries as twodigits in shifts (#157060)

* gh-156443: Keep PyLong loop carries as twodigits in shifts and division

* 📜🤖 Added by blurb_it.

* Update NEWS docs

* Update NEWS.d doc

* Update unit test and NEWS.d

* Revert changes to x_divrem

* Fix NEWS.d

* Apply batched suggestions from code review

Co-authored-by: Stan Ulbrych <[email protected]>

---------

Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com>
Co-authored-by: Stan Ulbrych <[email protected]>

files:
A 
Misc/NEWS.d/next/Core_and_Builtins/2026-09-07-09-52-21.gh-issue-156443.tT40sm.rst
M Lib/test/test_long.py
M Objects/longobject.c

diff --git a/Lib/test/test_long.py b/Lib/test/test_long.py
index 7c368e121024d2..c16bf1b1fe020e 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 00000000000000..54c345aad025ce
--- /dev/null
+++ 
b/Misc/NEWS.d/next/Core_and_Builtins/2026-09-07-09-52-21.gh-issue-156443.tT40sm.rst
@@ -0,0 +1 @@
+Speed up :func:`float` conversion and true division of large :class:`int` 
values.
diff --git a/Objects/longobject.c b/Objects/longobject.c
index 564f9601bbfde4..c96ed6a215b5a1 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

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to