Skip to content
This repository was archived by the owner on Jan 30, 2023. It is now read-only.

Commit 3e86d91

Browse files
committed
sage.arith.long.integer_check_py: fix for python 3.11
Previous code assumed `PyLong_SHIFT` is 15 on a 32-bit system, which is no longer true on python 3.11 (`PyLong_SHIFT` defaults to 30 now both on 32-bit and 64-bit systems). We now assume `PyLong_SHIFT <= BITS_IN_LONG <= 3 * PyLong_SHIFT`. This is true in all the default configurations: - BITS_IN_LONG = 63, PyLong_SHIFT = 30 - BITS_IN_LONG = 31, PyLong_SHIFT = 15 (python <= 3.10) - BITS_IN_LONG = 31, PyLong_SHIFT = 30 (new in python 3.11) The current code asserts `2 * PyLong_SHIFT <= BITS_IN_LONG` and indeed the implementation is broken when `BITS_IN_LONG < 2 * PyLong_SHIFT` since it doesn't handle overflow in the second digit. I changed the current code in the simplest possible way that fixes this.
1 parent 05f186f commit 3e86d91

File tree

1 file changed

+22
-7
lines changed

1 file changed

+22
-7
lines changed

src/sage/arith/long.pxd

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -291,20 +291,24 @@ cdef inline bint integer_check_long_py(x, long* value, int* err):
291291
cdef const digit* D = (<py_long>x).ob_digit
292292
cdef Py_ssize_t size = Py_SIZE(x)
293293

294-
# We assume that PyLong_SHIFT is 15 on a 32-bit system and 30 on a
295-
# 64-bit system. This is not guaranteed by Python, but it is the
296-
# default configuration.
294+
# We assume PyLong_SHIFT <= BITS_IN_LONG <= 3 * PyLong_SHIFT.
295+
# This is true in all the default configurations:
296+
# - BITS_IN_LONG = 63, PyLong_SHIFT = 30
297+
# - BITS_IN_LONG = 31, PyLong_SHIFT = 15 (python <= 3.10)
298+
# - BITS_IN_LONG = 31, PyLong_SHIFT = 30 (new in python 3.11)
299+
# cf. https://trac.sagemath.org/ticket/33842#comment:130
297300
#
298-
# This way, we know that 1 and 2 digits certainly fit in a C long
299-
# and 4 or more digits never fit. For 3 digits, we need an explicit
300-
# overflow check.
301+
# This way, we know that 1 digit certainly fits in a C long
302+
# and 4 or more digits never fit.
303+
# For 2 or 3 digits, we need an explicit overflow check.
301304
cdef int BITS_IN_LONG = 8 * sizeof(long) - 1
302-
if not (2 * PyLong_SHIFT <= BITS_IN_LONG < 4 * PyLong_SHIFT):
305+
if not (PyLong_SHIFT <= BITS_IN_LONG <= 3 * PyLong_SHIFT):
303306
raise AssertionError(
304307
f"PyLong_SHIFT = {PyLong_SHIFT}, "
305308
f"BITS_IN_LONG = {BITS_IN_LONG}")
306309

307310
cdef long lead
311+
cdef long lead_2_overflow = (<long>1) << (BITS_IN_LONG - PyLong_SHIFT)
308312
cdef long lead_3_overflow = (<long>1) << (BITS_IN_LONG - 2 * PyLong_SHIFT)
309313
if size == 0:
310314
value[0] = 0
@@ -316,9 +320,20 @@ cdef inline bint integer_check_long_py(x, long* value, int* err):
316320
value[0] = -dig(D, 0)
317321
err[0] = 0
318322
elif size == 2:
323+
if BITS_IN_LONG < 2 * PyLong_SHIFT and D[1] >= lead_2_overflow:
324+
err[0] = ERR_OVERFLOW
325+
return 1
319326
value[0] = dig(D, 0) + dig(D, 1)
320327
err[0] = 0
321328
elif size == -2:
329+
if BITS_IN_LONG < 2 * PyLong_SHIFT and D[1] >= lead_2_overflow:
330+
if D[0] == 0 and D[1] == lead_2_overflow:
331+
# Special case for LONG_MIN
332+
value[0] = (<long>-1) << BITS_IN_LONG
333+
err[0] = 0
334+
else:
335+
err[0] = ERR_OVERFLOW
336+
return 1
322337
value[0] = -(dig(D, 0) + dig(D, 1))
323338
err[0] = 0
324339
elif size == 3:

0 commit comments

Comments
 (0)