Skip to content

Commit 3581c7a

Browse files
bpo-46055: Speed up binary shifting operators (GH-30044)
Co-authored-by: Mark Dickinson <[email protected]>
1 parent 360fedc commit 3581c7a

File tree

4 files changed

+80
-3
lines changed

4 files changed

+80
-3
lines changed

Lib/test/test_long.py

+61-2
Original file line numberDiff line numberDiff line change
@@ -946,8 +946,13 @@ def test_huge_lshift(self, size):
946946
self.assertEqual(1 << (sys.maxsize + 1000), 1 << 1000 << sys.maxsize)
947947

948948
def test_huge_rshift(self):
949-
self.assertEqual(42 >> (1 << 1000), 0)
950-
self.assertEqual((-42) >> (1 << 1000), -1)
949+
huge_shift = 1 << 1000
950+
self.assertEqual(42 >> huge_shift, 0)
951+
self.assertEqual((-42) >> huge_shift, -1)
952+
self.assertEqual(1123 >> huge_shift, 0)
953+
self.assertEqual((-1123) >> huge_shift, -1)
954+
self.assertEqual(2**128 >> huge_shift, 0)
955+
self.assertEqual(-2**128 >> huge_shift, -1)
951956

952957
@support.cpython_only
953958
@support.bigmemtest(sys.maxsize + 500, memuse=2/15, dry_run=False)
@@ -956,6 +961,60 @@ def test_huge_rshift_of_huge(self, size):
956961
self.assertEqual(huge >> (sys.maxsize + 1), (1 << 499) + 5)
957962
self.assertEqual(huge >> (sys.maxsize + 1000), 0)
958963

964+
def test_small_rshift(self):
965+
self.assertEqual(42 >> 1, 21)
966+
self.assertEqual((-42) >> 1, -21)
967+
self.assertEqual(43 >> 1, 21)
968+
self.assertEqual((-43) >> 1, -22)
969+
970+
self.assertEqual(1122 >> 1, 561)
971+
self.assertEqual((-1122) >> 1, -561)
972+
self.assertEqual(1123 >> 1, 561)
973+
self.assertEqual((-1123) >> 1, -562)
974+
975+
self.assertEqual(2**128 >> 1, 2**127)
976+
self.assertEqual(-2**128 >> 1, -2**127)
977+
self.assertEqual((2**128 + 1) >> 1, 2**127)
978+
self.assertEqual(-(2**128 + 1) >> 1, -2**127 - 1)
979+
980+
def test_medium_rshift(self):
981+
self.assertEqual(42 >> 9, 0)
982+
self.assertEqual((-42) >> 9, -1)
983+
self.assertEqual(1122 >> 9, 2)
984+
self.assertEqual((-1122) >> 9, -3)
985+
self.assertEqual(2**128 >> 9, 2**119)
986+
self.assertEqual(-2**128 >> 9, -2**119)
987+
988+
def test_big_rshift(self):
989+
self.assertEqual(42 >> 32, 0)
990+
self.assertEqual((-42) >> 32, -1)
991+
self.assertEqual(1122 >> 32, 0)
992+
self.assertEqual((-1122) >> 32, -1)
993+
self.assertEqual(2**128 >> 32, 2**96)
994+
self.assertEqual(-2**128 >> 32, -2**96)
995+
996+
def test_small_lshift(self):
997+
self.assertEqual(42 << 1, 84)
998+
self.assertEqual((-42) << 1, -84)
999+
self.assertEqual(561 << 1, 1122)
1000+
self.assertEqual((-561) << 1, -1122)
1001+
self.assertEqual(2**127 << 1, 2**128)
1002+
self.assertEqual(-2**127 << 1, -2**128)
1003+
1004+
def test_medium_lshift(self):
1005+
self.assertEqual(42 << 9, 21504)
1006+
self.assertEqual((-42) << 9, -21504)
1007+
self.assertEqual(1122 << 9, 574464)
1008+
self.assertEqual((-1122) << 9, -574464)
1009+
1010+
def test_big_lshift(self):
1011+
self.assertEqual(42 << 32, 42 * 2**32)
1012+
self.assertEqual((-42) << 32, -42 * 2**32)
1013+
self.assertEqual(1122 << 32, 1122 * 2**32)
1014+
self.assertEqual((-1122) << 32, -1122 * 2**32)
1015+
self.assertEqual(2**128 << 32, 2**160)
1016+
self.assertEqual(-2**128 << 32, -2**160)
1017+
9591018
@support.cpython_only
9601019
def test_small_ints_in_huge_calculation(self):
9611020
a = 2 ** 100

Misc/ACKS

+1
Original file line numberDiff line numberDiff line change
@@ -1964,6 +1964,7 @@ Doug Wyatt
19641964
Xiang Zhang
19651965
Robert Xiao
19661966
Florent Xicluna
1967+
Xinhang Xu
19671968
Arnon Yaari
19681969
Alakshendra Yadav
19691970
Hirokazu Yamamoto
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Speed up shifting operation involving integers less than
2+
:c:macro:`PyLong_BASE`. Patch by Xinhang Xu.

Objects/longobject.c

+16-1
Original file line numberDiff line numberDiff line change
@@ -4493,6 +4493,15 @@ long_rshift1(PyLongObject *a, Py_ssize_t wordshift, digit remshift)
44934493
Py_ssize_t newsize, hishift, i, j;
44944494
twodigits accum;
44954495

4496+
if (IS_MEDIUM_VALUE(a)) {
4497+
stwodigits m, x;
4498+
digit shift;
4499+
m = medium_value(a);
4500+
shift = wordshift == 0 ? remshift : PyLong_SHIFT;
4501+
x = m < 0 ? ~(~m >> shift) : m >> shift;
4502+
return _PyLong_FromSTwoDigits(x);
4503+
}
4504+
44964505
if (Py_SIZE(a) < 0) {
44974506
/* Right shifting negative numbers is harder */
44984507
PyLongObject *a1, *a2;
@@ -4566,11 +4575,17 @@ _PyLong_Rshift(PyObject *a, size_t shiftby)
45664575
static PyObject *
45674576
long_lshift1(PyLongObject *a, Py_ssize_t wordshift, digit remshift)
45684577
{
4569-
/* This version due to Tim Peters */
45704578
PyLongObject *z = NULL;
45714579
Py_ssize_t oldsize, newsize, i, j;
45724580
twodigits accum;
45734581

4582+
if (wordshift == 0 && IS_MEDIUM_VALUE(a)) {
4583+
stwodigits m = medium_value(a);
4584+
// bypass undefined shift operator behavior
4585+
stwodigits x = m < 0 ? -(-m << remshift) : m << remshift;
4586+
return _PyLong_FromSTwoDigits(x);
4587+
}
4588+
45744589
oldsize = Py_ABS(Py_SIZE(a));
45754590
newsize = oldsize + wordshift;
45764591
if (remshift)

0 commit comments

Comments
 (0)