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.
This commit is contained in:
Serhiy Storchaka 2024-09-29 10:40:20 +03:00 committed by GitHub
parent e0a41a5dd1
commit d08c788822
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 108 additions and 175 deletions

View file

@ -1657,7 +1657,7 @@ math_isqrt(PyObject *module, PyObject *n)
/*[clinic end generated code: output=35a6f7f980beab26 input=5b6e7ae4fa6c43d6]*/
{
int a_too_large, c_bit_length;
uint64_t c, d;
int64_t c, d;
uint64_t m;
uint32_t u;
PyObject *a = NULL, *b;
@ -1680,14 +1680,13 @@ math_isqrt(PyObject *module, PyObject *n)
/* c = (n.bit_length() - 1) // 2 */
c = _PyLong_NumBits(n);
if (c == (uint64_t)(-1)) {
goto error;
}
c = (c - 1U) / 2U;
assert(c > 0);
assert(!PyErr_Occurred());
c = (c - 1) / 2;
/* Fast path: if c <= 31 then n < 2**64 and we can compute directly with a
fast, almost branch-free algorithm. */
if (c <= 31U) {
if (c <= 31) {
int shift = 31 - (int)c;
m = (uint64_t)PyLong_AsUnsignedLongLong(n);
Py_DECREF(n);
@ -1704,13 +1703,13 @@ math_isqrt(PyObject *module, PyObject *n)
/* From n >= 2**64 it follows that c.bit_length() >= 6. */
c_bit_length = 6;
while ((c >> c_bit_length) > 0U) {
while ((c >> c_bit_length) > 0) {
++c_bit_length;
}
/* Initialise d and a. */
d = c >> (c_bit_length - 5);
b = _PyLong_Rshift(n, 2U*c - 62U);
b = _PyLong_Rshift(n, 2*c - 62);
if (b == NULL) {
goto error;
}
@ -1727,12 +1726,12 @@ math_isqrt(PyObject *module, PyObject *n)
for (int s = c_bit_length - 6; s >= 0; --s) {
PyObject *q;
uint64_t e = d;
int64_t e = d;
d = c >> s;
/* q = (n >> 2*c - e - d + 1) // a */
q = _PyLong_Rshift(n, 2U*c - d - e + 1U);
q = _PyLong_Rshift(n, 2*c - d - e + 1);
if (q == NULL) {
goto error;
}
@ -1742,7 +1741,7 @@ math_isqrt(PyObject *module, PyObject *n)
}
/* a = (a << d - 1 - e) + q */
Py_SETREF(a, _PyLong_Lshift(a, d - 1U - e));
Py_SETREF(a, _PyLong_Lshift(a, d - 1 - e));
if (a == NULL) {
Py_DECREF(q);
goto error;
@ -2202,8 +2201,8 @@ loghelper(PyObject* arg, double (*func)(double))
to compute the log anyway. Clear the exception and continue. */
PyErr_Clear();
x = _PyLong_Frexp((PyLongObject *)arg, &e);
if (x == -1.0 && PyErr_Occurred())
return NULL;
assert(e >= 0);
assert(!PyErr_Occurred());
/* Value is ~= x * 2**e, so the log ~= log(x) + log(2) * e. */
result = func(x) + func(2.0) * e;
}