Skip to content

Commit d08c788

Browse files
gh-123497: New limit for Python integers on 64-bit platforms (GH-123724)
Instead of be limited just by the size of addressable memory (2**63 bytes), Python integers are now also limited by the number of bits, so the number of bit now always fit in a 64-bit integer. Both limits are much larger than what might be available in practice, so it doesn't affect users. _PyLong_NumBits() and _PyLong_Frexp() are now always successful.
1 parent e0a41a5 commit d08c788

File tree

8 files changed

+108
-175
lines changed

8 files changed

+108
-175
lines changed

Include/cpython/longobject.h

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,10 +71,9 @@ 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-
(uint64_t)-1 is returned and OverflowError set if the true result doesn't
75-
fit in a size_t.
74+
Always successful.
7675
*/
77-
PyAPI_FUNC(uint64_t) _PyLong_NumBits(PyObject *v);
76+
PyAPI_FUNC(int64_t) _PyLong_NumBits(PyObject *v);
7877

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

Include/internal/pycore_long.h

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -79,11 +79,10 @@ static inline PyObject* _PyLong_FromUnsignedChar(unsigned char i)
7979
}
8080

8181
// _PyLong_Frexp returns a double x and an exponent e such that the
82-
// true value is approximately equal to x * 2**e. e is >= 0. x is
82+
// true value is approximately equal to x * 2**e. x is
8383
// 0.0 if and only if the input is 0 (in which case, e and x are both
84-
// zeroes); otherwise, 0.5 <= abs(x) < 1.0. On overflow, which is
85-
// possible if the number of bits doesn't fit into a Py_ssize_t, sets
86-
// OverflowError and returns -1.0 for x, 0 for e.
84+
// zeroes); otherwise, 0.5 <= abs(x) < 1.0.
85+
// Always successful.
8786
//
8887
// Export for 'math' shared extension
8988
PyAPI_DATA(double) _PyLong_Frexp(PyLongObject *a, int64_t *e);
@@ -105,10 +104,10 @@ PyAPI_DATA(PyObject*) _PyLong_DivmodNear(PyObject *, PyObject *);
105104
PyAPI_DATA(PyObject*) _PyLong_Format(PyObject *obj, int base);
106105

107106
// Export for 'math' shared extension
108-
PyAPI_DATA(PyObject*) _PyLong_Rshift(PyObject *, uint64_t);
107+
PyAPI_DATA(PyObject*) _PyLong_Rshift(PyObject *, int64_t);
109108

110109
// Export for 'math' shared extension
111-
PyAPI_DATA(PyObject*) _PyLong_Lshift(PyObject *, uint64_t);
110+
PyAPI_DATA(PyObject*) _PyLong_Lshift(PyObject *, int64_t);
112111

113112
PyAPI_FUNC(PyObject*) _PyLong_Add(PyLongObject *left, PyLongObject *right);
114113
PyAPI_FUNC(PyObject*) _PyLong_Multiply(PyLongObject *left, PyLongObject *right);

Modules/_pickle.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2146,7 +2146,7 @@ save_long(PicklerObject *self, PyObject *obj)
21462146

21472147
if (self->proto >= 2) {
21482148
/* Linear-time pickling. */
2149-
uint64_t nbits;
2149+
int64_t nbits;
21502150
size_t nbytes;
21512151
unsigned char *pdata;
21522152
char header[5];
@@ -2161,8 +2161,8 @@ save_long(PicklerObject *self, PyObject *obj)
21612161
return 0;
21622162
}
21632163
nbits = _PyLong_NumBits(obj);
2164-
if (nbits == (uint64_t)-1 && PyErr_Occurred())
2165-
goto error;
2164+
assert(nbits >= 0);
2165+
assert(!PyErr_Occurred());
21662166
/* How many bytes do we need? There are nbits >> 3 full
21672167
* bytes of data, and nbits & 7 leftover bits. If there
21682168
* are any leftover bits, then we clearly need another

Modules/_randommodule.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,7 @@ random_seed(RandomObject *self, PyObject *arg)
295295
int result = -1; /* guilty until proved innocent */
296296
PyObject *n = NULL;
297297
uint32_t *key = NULL;
298-
uint64_t bits;
298+
int64_t bits;
299299
size_t keyused;
300300
int res;
301301

@@ -335,8 +335,8 @@ random_seed(RandomObject *self, PyObject *arg)
335335

336336
/* Now split n into 32-bit chunks, from the right. */
337337
bits = _PyLong_NumBits(n);
338-
if (bits == (uint64_t)-1 && PyErr_Occurred())
339-
goto Done;
338+
assert(bits >= 0);
339+
assert(!PyErr_Occurred());
340340

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

Modules/mathmodule.c

Lines changed: 12 additions & 13 deletions
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-
uint64_t c, d;
1660+
int64_t c, d;
16611661
uint64_t m;
16621662
uint32_t u;
16631663
PyObject *a = NULL, *b;
@@ -1680,14 +1680,13 @@ math_isqrt(PyObject *module, PyObject *n)
16801680

16811681
/* c = (n.bit_length() - 1) // 2 */
16821682
c = _PyLong_NumBits(n);
1683-
if (c == (uint64_t)(-1)) {
1684-
goto error;
1685-
}
1686-
c = (c - 1U) / 2U;
1683+
assert(c > 0);
1684+
assert(!PyErr_Occurred());
1685+
c = (c - 1) / 2;
16871686

16881687
/* Fast path: if c <= 31 then n < 2**64 and we can compute directly with a
16891688
fast, almost branch-free algorithm. */
1690-
if (c <= 31U) {
1689+
if (c <= 31) {
16911690
int shift = 31 - (int)c;
16921691
m = (uint64_t)PyLong_AsUnsignedLongLong(n);
16931692
Py_DECREF(n);
@@ -1704,13 +1703,13 @@ math_isqrt(PyObject *module, PyObject *n)
17041703

17051704
/* From n >= 2**64 it follows that c.bit_length() >= 6. */
17061705
c_bit_length = 6;
1707-
while ((c >> c_bit_length) > 0U) {
1706+
while ((c >> c_bit_length) > 0) {
17081707
++c_bit_length;
17091708
}
17101709

17111710
/* Initialise d and a. */
17121711
d = c >> (c_bit_length - 5);
1713-
b = _PyLong_Rshift(n, 2U*c - 62U);
1712+
b = _PyLong_Rshift(n, 2*c - 62);
17141713
if (b == NULL) {
17151714
goto error;
17161715
}
@@ -1727,12 +1726,12 @@ math_isqrt(PyObject *module, PyObject *n)
17271726

17281727
for (int s = c_bit_length - 6; s >= 0; --s) {
17291728
PyObject *q;
1730-
uint64_t e = d;
1729+
int64_t e = d;
17311730

17321731
d = c >> s;
17331732

17341733
/* q = (n >> 2*c - e - d + 1) // a */
1735-
q = _PyLong_Rshift(n, 2U*c - d - e + 1U);
1734+
q = _PyLong_Rshift(n, 2*c - d - e + 1);
17361735
if (q == NULL) {
17371736
goto error;
17381737
}
@@ -1742,7 +1741,7 @@ math_isqrt(PyObject *module, PyObject *n)
17421741
}
17431742

17441743
/* a = (a << d - 1 - e) + q */
1745-
Py_SETREF(a, _PyLong_Lshift(a, d - 1U - e));
1744+
Py_SETREF(a, _PyLong_Lshift(a, d - 1 - e));
17461745
if (a == NULL) {
17471746
Py_DECREF(q);
17481747
goto error;
@@ -2202,8 +2201,8 @@ loghelper(PyObject* arg, double (*func)(double))
22022201
to compute the log anyway. Clear the exception and continue. */
22032202
PyErr_Clear();
22042203
x = _PyLong_Frexp((PyLongObject *)arg, &e);
2205-
if (x == -1.0 && PyErr_Occurred())
2206-
return NULL;
2204+
assert(e >= 0);
2205+
assert(!PyErr_Occurred());
22072206
/* Value is ~= x * 2**e, so the log ~= log(x) + log(2) * e. */
22082207
result = func(x) + func(2.0) * e;
22092208
}

Objects/floatobject.c

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -406,19 +406,16 @@ float_richcompare(PyObject *v, PyObject *w, int op)
406406
}
407407
/* The signs are the same. */
408408
/* Convert w to a double if it fits. In particular, 0 fits. */
409-
uint64_t nbits64 = _PyLong_NumBits(w);
410-
if (nbits64 > (unsigned int)DBL_MAX_EXP) {
409+
int64_t nbits64 = _PyLong_NumBits(w);
410+
assert(nbits64 >= 0);
411+
assert(!PyErr_Occurred());
412+
if (nbits64 > DBL_MAX_EXP) {
411413
/* This Python integer is larger than any finite C double.
412414
* Replace with little doubles
413415
* that give the same outcome -- w is so large that
414416
* its magnitude must exceed the magnitude of any
415417
* finite float.
416418
*/
417-
if (nbits64 == (uint64_t)-1 && PyErr_Occurred()) {
418-
/* This Python integer is so large that uint64_t isn't
419-
* big enough to hold the # of bits. */
420-
PyErr_Clear();
421-
}
422419
i = (double)vsign;
423420
assert(wsign != 0);
424421
j = wsign * 2.0;

0 commit comments

Comments
 (0)