tests: Add specific tests for "long long" 64-bit bigints.

These will run on all ports which support them, but importantly
they'll also run on ports that don't support arbitrary precision
but do support 64-bit long ints.

Includes some test workarounds to account for things which will overflow
once "long long" big integers overflow (added in follow-up commit):

- uctypes_array_load_store test was failing already, now won't parse.
- all the ffi_int tests contain 64-bit unsigned values, that won't parse
  as long long.

This work was funded through GitHub Sponsors.

Signed-off-by: Angus Gratton <angus@redyak.com.au>
This commit is contained in:
Angus Gratton
2025-03-18 12:07:00 +11:00
committed by Damien George
parent c72a3e528d
commit 2d8d64059f
5 changed files with 149 additions and 1 deletions

View File

@@ -0,0 +1,126 @@
# test support for 64-bit long integers
# (some ports don't support arbitrary precision but do support these)
# this test is adapted from int_big1.py with numbers kept within 64-bit signed range
# to test arbitrary precision integers
x = 1000000000000000000
xn = -1000000000000000000
y = 2000000000000000000
# printing
print(x)
print(y)
print('%#X' % (x - x)) # print prefix
print('{:#,}'.format(x)) # print with commas
# construction
print(int(x))
# addition
print(x + 1)
print(x + y)
print(x + xn == 0)
print(bool(x + xn))
# subtraction
print(x - 1)
print(x - y)
print(y - x)
print(x - x == 0)
print(bool(x - x))
# multiplication
print(x * 2)
print(1090511627776 * 1048500)
# integer division
print(x // 2)
print(y // x)
# bit inversion
print(~x)
print(~(-x))
# left shift
print("left shift positive")
x = 0x40000000
for i in range(32):
x = x << 1
print(x)
# right shift
print("right shift positive")
x = 0x2000000000000000 # TODO: why can't second-tip bit be set?
for i in range(64):
x = x >> 1
print(x)
# left shift of a negative number
print("left shift negative")
for i in range(8):
print(-10000000000000000 << i)
print(-10000000000000001 << i)
print(-10000000000000002 << i)
print(-10000000000000003 << i)
print(-10000000000000004 << i)
# right shift of a negative number
print("right shift negative")
for i in range(8):
print(-1000000000000000000 >> i)
print(-1000000000000000001 >> i)
print(-1000000000000000002 >> i)
print(-1000000000000000003 >> i)
print(-1000000000000000004 >> i)
# conversion from string
print(int("1234567890123456789"))
print(int("-1234567890123456789"))
print(int("1234567890abcdef", 16))
print(int("1234567890ABCDEF", 16))
print(int("-1234567890ABCDEF", 16))
print(int("ijklmnopqrsz", 36))
# numbers close to 64-bit limits
print(int("-9111222333444555666"))
print(int("9111222333444555666"))
# numbers with preceding 0s
print(int("-00000000000000000000009111222333444555666"))
print(int("0000000000000000000000009111222333444555666"))
# invalid characters in string
try:
print(int("1234567890abcdef"))
except ValueError:
print('ValueError');
try:
print(int("123456789\x01"))
except ValueError:
print('ValueError');
# test parsing ints just on threshold of small to big
# for 32 bit archs
x = 1073741823 # small
x = -1073741823 # small
x = 1073741824 # big
x = -1073741824 # big
# for 64 bit archs
x = 4611686018427387903 # small
x = -4611686018427387903 # small
x = 4611686018427387904 # big
x = -4611686018427387904 # big
# sys.maxsize is a constant bigint, so test it's compatible with dynamic ones
import sys
if hasattr(sys, "maxsize"):
print(sys.maxsize + 1 - 1 == sys.maxsize)
else:
print(True) # No maxsize property in this config
# test extraction of big int value via mp_obj_get_int_maybe
x = 1 << 62
print('a' * (x + 4 - x))

View File

@@ -6,6 +6,13 @@ except ImportError:
print("SKIP")
raise SystemExit
# 'int' needs to be able to represent UINT64 for this test
try:
int("FF" * 8, 16)
except OverflowError:
print("SKIP")
raise SystemExit
N = 5
for endian in ("NATIVE", "LITTLE_ENDIAN", "BIG_ENDIAN"):

View File

@@ -0,0 +1,2 @@
# Check whether 64-bit long integers are supported
print(1 << 62)

View File

@@ -0,0 +1 @@
4611686018427387904

View File

@@ -628,6 +628,7 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1):
skip_tests = set()
skip_native = False
skip_int_big = False
skip_int_64 = False
skip_bytearray = False
skip_set_type = False
skip_slice = False
@@ -658,6 +659,11 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1):
if output != b"1000000000000000000000000000000000000000000000\n":
skip_int_big = True
# Check if 'long long' precision integers are supported, even if arbitrary precision is not
output = run_feature_check(pyb, args, "int_64.py")
if output != b"4611686018427387904\n":
skip_int_64 = True
# Check if bytearray is supported, and skip such tests if it's not
output = run_feature_check(pyb, args, "bytearray.py")
if output != b"bytearray\n":
@@ -885,7 +891,12 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1):
test_name = os.path.splitext(os.path.basename(test_file))[0]
is_native = test_name.startswith("native_") or test_name.startswith("viper_")
is_endian = test_name.endswith("_endian")
is_int_big = test_name.startswith("int_big") or test_name.endswith("_intbig")
is_int_big = (
test_name.startswith("int_big")
or test_name.endswith("_intbig")
or test_name.startswith("ffi_int") # these tests contain large integer literals
)
is_int_64 = test_name.startswith("int_64") or test_name.endswith("_int64")
is_bytearray = test_name.startswith("bytearray") or test_name.endswith("_bytearray")
is_set_type = test_name.startswith(("set_", "frozenset")) or test_name.endswith("_set")
is_slice = test_name.find("slice") != -1 or test_name in misc_slice_tests
@@ -899,6 +910,7 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1):
skip_it |= skip_native and is_native
skip_it |= skip_endian and is_endian
skip_it |= skip_int_big and is_int_big
skip_it |= skip_int_64 and is_int_64
skip_it |= skip_bytearray and is_bytearray
skip_it |= skip_set_type and is_set_type
skip_it |= skip_slice and is_slice