Skip to content

Commit 32c7dbb

Browse files
gh-121485: Always use 64-bit integers for integers bits count (GH-121486)
Use 64-bit integers instead of platform specific size_t or Py_ssize_t to represent the number of bits in Python integer.
1 parent 58ce131 commit 32c7dbb

File tree

13 files changed

+187
-111
lines changed

13 files changed

+187
-111
lines changed

Include/cpython/longobject.h

+2-2
Original file line numberDiff line numberDiff line change
@@ -71,10 +71,10 @@ PyAPI_FUNC(int) _PyLong_Sign(PyObject *v);
7171
absolute value of a long. For example, this returns 1 for 1 and -1, 2
7272
for 2 and -2, and 2 for 3 and -3. It returns 0 for 0.
7373
v must not be NULL, and must be a normalized long.
74-
(size_t)-1 is returned and OverflowError set if the true result doesn't
74+
(uint64_t)-1 is returned and OverflowError set if the true result doesn't
7575
fit in a size_t.
7676
*/
77-
PyAPI_FUNC(size_t) _PyLong_NumBits(PyObject *v);
77+
PyAPI_FUNC(uint64_t) _PyLong_NumBits(PyObject *v);
7878

7979
/* _PyLong_FromByteArray: View the n unsigned bytes as a binary integer in
8080
base 256, and return a Python int with the same numeric value.

Include/internal/pycore_long.h

+3-3
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ static inline PyObject* _PyLong_FromUnsignedChar(unsigned char i)
8686
// OverflowError and returns -1.0 for x, 0 for e.
8787
//
8888
// Export for 'math' shared extension
89-
PyAPI_DATA(double) _PyLong_Frexp(PyLongObject *a, Py_ssize_t *e);
89+
PyAPI_DATA(double) _PyLong_Frexp(PyLongObject *a, int64_t *e);
9090

9191
extern PyObject* _PyLong_FromBytes(const char *, Py_ssize_t, int);
9292

@@ -105,10 +105,10 @@ PyAPI_DATA(PyObject*) _PyLong_DivmodNear(PyObject *, PyObject *);
105105
PyAPI_DATA(PyObject*) _PyLong_Format(PyObject *obj, int base);
106106

107107
// Export for 'math' shared extension
108-
PyAPI_DATA(PyObject*) _PyLong_Rshift(PyObject *, size_t);
108+
PyAPI_DATA(PyObject*) _PyLong_Rshift(PyObject *, uint64_t);
109109

110110
// Export for 'math' shared extension
111-
PyAPI_DATA(PyObject*) _PyLong_Lshift(PyObject *, size_t);
111+
PyAPI_DATA(PyObject*) _PyLong_Lshift(PyObject *, uint64_t);
112112

113113
PyAPI_FUNC(PyObject*) _PyLong_Add(PyLongObject *left, PyLongObject *right);
114114
PyAPI_FUNC(PyObject*) _PyLong_Multiply(PyLongObject *left, PyLongObject *right);

Lib/test/test_capi/test_long.py

+12
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,18 @@ def test_long_aspid(self):
340340
def test_long_aspid_limited(self):
341341
self._test_long_aspid(_testlimitedcapi.pylong_aspid)
342342

343+
@support.bigmemtest(2**32, memuse=0.35)
344+
def test_long_asnativebytes_huge(self, size):
345+
asnativebytes = _testcapi.pylong_asnativebytes
346+
v = 1 << size
347+
buffer = bytearray(size * 2 // 15 + 10)
348+
r = asnativebytes(v, buffer, 0, -1)
349+
self.assertEqual(r, size // 8 + 1)
350+
self.assertEqual(buffer.count(0), len(buffer))
351+
r = asnativebytes(v, buffer, len(buffer), -1)
352+
self.assertEqual(r, size // 8 + 1)
353+
self.assertEqual(buffer.count(0), len(buffer) - 1)
354+
343355
def test_long_asnativebytes(self):
344356
import math
345357
from _testcapi import (

Lib/test/test_long.py

+67-6
Original file line numberDiff line numberDiff line change
@@ -473,6 +473,12 @@ def test_float_conversion(self):
473473
self.check_float_conversion(value)
474474
self.check_float_conversion(-value)
475475

476+
@support.requires_IEEE_754
477+
@support.bigmemtest(2**32, memuse=0.2)
478+
def test_float_conversion_huge_integer(self, size):
479+
v = 1 << size
480+
self.assertRaises(OverflowError, float, v)
481+
476482
def test_float_overflow(self):
477483
for x in -2.0, -1.0, 0.0, 1.0, 2.0:
478484
self.assertEqual(float(int(x)), x)
@@ -614,6 +620,56 @@ def __lt__(self, other):
614620
eq(x > y, Rcmp > 0)
615621
eq(x >= y, Rcmp >= 0)
616622

623+
@support.requires_IEEE_754
624+
@support.bigmemtest(2**32, memuse=0.2)
625+
def test_mixed_compares_huge_integer(self, size):
626+
v = 1 << size
627+
f = sys.float_info.max
628+
self.assertIs(f == v, False)
629+
self.assertIs(f != v, True)
630+
self.assertIs(f < v, True)
631+
self.assertIs(f <= v, True)
632+
self.assertIs(f > v, False)
633+
self.assertIs(f >= v, False)
634+
f = float('inf')
635+
self.assertIs(f == v, False)
636+
self.assertIs(f != v, True)
637+
self.assertIs(f < v, False)
638+
self.assertIs(f <= v, False)
639+
self.assertIs(f > v, True)
640+
self.assertIs(f >= v, True)
641+
f = float('nan')
642+
self.assertIs(f == v, False)
643+
self.assertIs(f != v, True)
644+
self.assertIs(f < v, False)
645+
self.assertIs(f <= v, False)
646+
self.assertIs(f > v, False)
647+
self.assertIs(f >= v, False)
648+
649+
del v
650+
v = (-1) << size
651+
f = -sys.float_info.max
652+
self.assertIs(f == v, False)
653+
self.assertIs(f != v, True)
654+
self.assertIs(f < v, False)
655+
self.assertIs(f <= v, False)
656+
self.assertIs(f > v, True)
657+
self.assertIs(f >= v, True)
658+
f = float('-inf')
659+
self.assertIs(f == v, False)
660+
self.assertIs(f != v, True)
661+
self.assertIs(f < v, True)
662+
self.assertIs(f <= v, True)
663+
self.assertIs(f > v, False)
664+
self.assertIs(f >= v, False)
665+
f = float('nan')
666+
self.assertIs(f == v, False)
667+
self.assertIs(f != v, True)
668+
self.assertIs(f < v, False)
669+
self.assertIs(f <= v, False)
670+
self.assertIs(f > v, False)
671+
self.assertIs(f >= v, False)
672+
617673
def test__format__(self):
618674
self.assertEqual(format(123456789, 'd'), '123456789')
619675
self.assertEqual(format(123456789, 'd'), '123456789')
@@ -933,9 +989,12 @@ def test_huge_lshift_of_zero(self):
933989
self.assertEqual(0 << (sys.maxsize + 1), 0)
934990

935991
@support.cpython_only
936-
@support.bigmemtest(sys.maxsize + 1000, memuse=2/15 * 2, dry_run=False)
992+
@support.bigmemtest(2**32, memuse=0.2)
937993
def test_huge_lshift(self, size):
938-
self.assertEqual(1 << (sys.maxsize + 1000), 1 << 1000 << sys.maxsize)
994+
v = 5 << size
995+
self.assertEqual(v.bit_length(), size + 3)
996+
self.assertEqual(v.bit_count(), 2)
997+
self.assertEqual(v >> size, 5)
939998

940999
def test_huge_rshift(self):
9411000
huge_shift = 1 << 1000
@@ -947,11 +1006,13 @@ def test_huge_rshift(self):
9471006
self.assertEqual(-2**128 >> huge_shift, -1)
9481007

9491008
@support.cpython_only
950-
@support.bigmemtest(sys.maxsize + 500, memuse=2/15, dry_run=False)
1009+
@support.bigmemtest(2**32, memuse=0.2)
9511010
def test_huge_rshift_of_huge(self, size):
952-
huge = ((1 << 500) + 11) << sys.maxsize
953-
self.assertEqual(huge >> (sys.maxsize + 1), (1 << 499) + 5)
954-
self.assertEqual(huge >> (sys.maxsize + 1000), 0)
1011+
huge = ((1 << 500) + 11) << size
1012+
self.assertEqual(huge.bit_length(), size + 501)
1013+
self.assertEqual(huge.bit_count(), 4)
1014+
self.assertEqual(huge >> (size + 1), (1 << 499) + 5)
1015+
self.assertEqual(huge >> (size + 1000), 0)
9551016

9561017
def test_small_rshift(self):
9571018
self.assertEqual(42 >> 1, 21)

Lib/test/test_math.py

+16
Original file line numberDiff line numberDiff line change
@@ -1120,6 +1120,15 @@ def __index__(self):
11201120
with self.assertRaises(TypeError):
11211121
math.isqrt(value)
11221122

1123+
@support.bigmemtest(2**32, memuse=0.85)
1124+
def test_isqrt_huge(self, size):
1125+
if size & 1:
1126+
size += 1
1127+
v = 1 << size
1128+
w = math.isqrt(v)
1129+
self.assertEqual(w.bit_length(), size // 2 + 1)
1130+
self.assertEqual(w.bit_count(), 1)
1131+
11231132
def test_lcm(self):
11241133
lcm = math.lcm
11251134
self.assertEqual(lcm(0, 0), 0)
@@ -1261,6 +1270,13 @@ def testLog10(self):
12611270
self.assertEqual(math.log(INF), INF)
12621271
self.assertTrue(math.isnan(math.log10(NAN)))
12631272

1273+
@support.bigmemtest(2**32, memuse=0.2)
1274+
def test_log_huge_integer(self, size):
1275+
v = 1 << size
1276+
self.assertAlmostEqual(math.log2(v), size)
1277+
self.assertAlmostEqual(math.log(v), size * 0.6931471805599453)
1278+
self.assertAlmostEqual(math.log10(v), size * 0.3010299956639812)
1279+
12641280
def testSumProd(self):
12651281
sumprod = math.sumprod
12661282
Decimal = decimal.Decimal
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
:mod:`math` functions :func:`~math.isqrt`, :func:`~math.log`, :func:`~math.log2` and
2+
:func:`~math.log10` now support integers larger than ``2**2**32`` on 32-bit
3+
platforms.

Modules/_pickle.c

+3-3
Original file line numberDiff line numberDiff line change
@@ -2140,7 +2140,7 @@ save_long(PicklerObject *self, PyObject *obj)
21402140

21412141
if (self->proto >= 2) {
21422142
/* Linear-time pickling. */
2143-
size_t nbits;
2143+
uint64_t nbits;
21442144
size_t nbytes;
21452145
unsigned char *pdata;
21462146
char header[5];
@@ -2155,7 +2155,7 @@ save_long(PicklerObject *self, PyObject *obj)
21552155
return 0;
21562156
}
21572157
nbits = _PyLong_NumBits(obj);
2158-
if (nbits == (size_t)-1 && PyErr_Occurred())
2158+
if (nbits == (uint64_t)-1 && PyErr_Occurred())
21592159
goto error;
21602160
/* How many bytes do we need? There are nbits >> 3 full
21612161
* bytes of data, and nbits & 7 leftover bits. If there
@@ -2171,7 +2171,7 @@ save_long(PicklerObject *self, PyObject *obj)
21712171
* for in advance, though, so we always grab an extra
21722172
* byte at the start, and cut it back later if possible.
21732173
*/
2174-
nbytes = (nbits >> 3) + 1;
2174+
nbytes = (size_t)((nbits >> 3) + 1);
21752175
if (nbytes > 0x7fffffffL) {
21762176
PyErr_SetString(PyExc_OverflowError,
21772177
"int too large to pickle");

Modules/_randommodule.c

+4-3
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,8 @@ random_seed(RandomObject *self, PyObject *arg)
295295
int result = -1; /* guilty until proved innocent */
296296
PyObject *n = NULL;
297297
uint32_t *key = NULL;
298-
size_t bits, keyused;
298+
uint64_t bits;
299+
size_t keyused;
299300
int res;
300301

301302
if (arg == NULL || arg == Py_None) {
@@ -334,11 +335,11 @@ random_seed(RandomObject *self, PyObject *arg)
334335

335336
/* Now split n into 32-bit chunks, from the right. */
336337
bits = _PyLong_NumBits(n);
337-
if (bits == (size_t)-1 && PyErr_Occurred())
338+
if (bits == (uint64_t)-1 && PyErr_Occurred())
338339
goto Done;
339340

340341
/* Figure out how many 32-bit chunks this gives us. */
341-
keyused = bits == 0 ? 1 : (bits - 1) / 32 + 1;
342+
keyused = bits == 0 ? 1 : (size_t)((bits - 1) / 32 + 1);
342343

343344
/* Convert seed to byte sequence. */
344345
key = (uint32_t *)PyMem_Malloc((size_t)4 * keyused);

Modules/_testinternalcapi.c

+2-2
Original file line numberDiff line numberDiff line change
@@ -1853,7 +1853,7 @@ _testinternalcapi_test_long_numbits_impl(PyObject *module)
18531853
{
18541854
struct triple {
18551855
long input;
1856-
size_t nbits;
1856+
uint64_t nbits;
18571857
int sign;
18581858
} testcases[] = {{0, 0, 0},
18591859
{1L, 1, 1},
@@ -1873,7 +1873,7 @@ _testinternalcapi_test_long_numbits_impl(PyObject *module)
18731873
size_t i;
18741874

18751875
for (i = 0; i < Py_ARRAY_LENGTH(testcases); ++i) {
1876-
size_t nbits;
1876+
uint64_t nbits;
18771877
int sign;
18781878
PyObject *plong;
18791879

Modules/mathmodule.c

+4-4
Original file line numberDiff line numberDiff line change
@@ -1657,7 +1657,7 @@ math_isqrt(PyObject *module, PyObject *n)
16571657
/*[clinic end generated code: output=35a6f7f980beab26 input=5b6e7ae4fa6c43d6]*/
16581658
{
16591659
int a_too_large, c_bit_length;
1660-
size_t c, d;
1660+
uint64_t c, d;
16611661
uint64_t m;
16621662
uint32_t u;
16631663
PyObject *a = NULL, *b;
@@ -1680,7 +1680,7 @@ math_isqrt(PyObject *module, PyObject *n)
16801680

16811681
/* c = (n.bit_length() - 1) // 2 */
16821682
c = _PyLong_NumBits(n);
1683-
if (c == (size_t)(-1)) {
1683+
if (c == (uint64_t)(-1)) {
16841684
goto error;
16851685
}
16861686
c = (c - 1U) / 2U;
@@ -1727,7 +1727,7 @@ math_isqrt(PyObject *module, PyObject *n)
17271727

17281728
for (int s = c_bit_length - 6; s >= 0; --s) {
17291729
PyObject *q;
1730-
size_t e = d;
1730+
uint64_t e = d;
17311731

17321732
d = c >> s;
17331733

@@ -2185,7 +2185,7 @@ loghelper(PyObject* arg, double (*func)(double))
21852185
/* If it is int, do it ourselves. */
21862186
if (PyLong_Check(arg)) {
21872187
double x, result;
2188-
Py_ssize_t e;
2188+
int64_t e;
21892189

21902190
/* Negative or zero inputs give a ValueError. */
21912191
if (!_PyLong_IsPositive((PyLongObject *)arg)) {

Objects/floatobject.c

+12-8
Original file line numberDiff line numberDiff line change
@@ -399,7 +399,6 @@ float_richcompare(PyObject *v, PyObject *w, int op)
399399
else if (PyLong_Check(w)) {
400400
int vsign = i == 0.0 ? 0 : i < 0.0 ? -1 : 1;
401401
int wsign = _PyLong_Sign(w);
402-
size_t nbits;
403402
int exponent;
404403

405404
if (vsign != wsign) {
@@ -412,20 +411,25 @@ float_richcompare(PyObject *v, PyObject *w, int op)
412411
}
413412
/* The signs are the same. */
414413
/* Convert w to a double if it fits. In particular, 0 fits. */
415-
nbits = _PyLong_NumBits(w);
416-
if (nbits == (size_t)-1 && PyErr_Occurred()) {
417-
/* This long is so large that size_t isn't big enough
418-
* to hold the # of bits. Replace with little doubles
414+
uint64_t nbits64 = _PyLong_NumBits(w);
415+
if (nbits64 > (unsigned int)DBL_MAX_EXP) {
416+
/* This Python integer is larger than any finite C double.
417+
* Replace with little doubles
419418
* that give the same outcome -- w is so large that
420419
* its magnitude must exceed the magnitude of any
421420
* finite float.
422421
*/
423-
PyErr_Clear();
422+
if (nbits64 == (uint64_t)-1 && PyErr_Occurred()) {
423+
/* This Python integer is so large that uint64_t isn't
424+
* big enough to hold the # of bits. */
425+
PyErr_Clear();
426+
}
424427
i = (double)vsign;
425428
assert(wsign != 0);
426429
j = wsign * 2.0;
427430
goto Compare;
428431
}
432+
int nbits = (int)nbits64;
429433
if (nbits <= 48) {
430434
j = PyLong_AsDouble(w);
431435
/* It's impossible that <= 48 bits overflowed. */
@@ -449,12 +453,12 @@ float_richcompare(PyObject *v, PyObject *w, int op)
449453
/* exponent is the # of bits in v before the radix point;
450454
* we know that nbits (the # of bits in w) > 48 at this point
451455
*/
452-
if (exponent < 0 || (size_t)exponent < nbits) {
456+
if (exponent < nbits) {
453457
i = 1.0;
454458
j = 2.0;
455459
goto Compare;
456460
}
457-
if ((size_t)exponent > nbits) {
461+
if (exponent > nbits) {
458462
i = 2.0;
459463
j = 1.0;
460464
goto Compare;

0 commit comments

Comments
 (0)