I'm looking for a way to get (using Python) the maximum and minimum values of C types integers (ie uint8
, int8
, uint16
, int16
, uint32
, int32
, uint64
, int64
...) from Python.
I was expecting to find this in ctypes
module
In [1]: import ctypes
In [2]: ctypes.c_uint8(256)
Out[2]: c_ubyte(0)
In [3]: ctypes.c_uint8(-1)
Out[3]: c_ubyte(255)
but I couldn't find it.
Julia have great feature for this:
julia> typemax(UInt8)
0xff
julia> typemin(UInt8)
0x00
julia> typemin(Int8)
-128
julia> typemax(Int8)
127
I'm pretty sure Python have something quite similar.
Ideally I'm even looking for a way to ensure that a given Python integer (which is said to be unbounded) can be converted safely in a C type integer of a given size. When number is not in expected interval, it should raise an exception.
Currently overflow doesn't raise exception:
In [4]: ctypes.c_uint8(256)
Out[4]: c_ubyte(0)
I saw this SO post Maximum and Minimum values for ints but it's a bit different as author is looking for min/max value of a Python integer... not a C integer (from Python)
I also noticed Detecting C types limits ("limits.h") in python? but, even if it's quite related, it doesn't really answer my question.
According to: [Python.Docs]: Numeric Types - int, float, complex:
Integers have unlimited precision.
Translated to code:
>>> i = 10 ** 100 >>> i 10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 >>> len(str(i)) 101 >>> i.bit_length() 333
On the other hand, each C type has a fixed size (depending on platform / architecture), as clearly shown in [CPPReference]: Fundamental types.
Since [Python.Docs]: ctypes - A foreign function library for Python doesn't mention anything about types limits (note that there is some stuff not documented there), let's find them out manually.
code00.py:
#!/usr/bin/env python3
import sys
from ctypes import (
c_int8,
c_uint8,
c_byte,
c_ubyte,
c_int16,
c_uint16,
c_short,
c_ushort,
c_int32,
c_uint32,
c_int,
c_uint,
c_long,
c_ulong,
c_longlong,
c_ulonglong,
c_int64,
c_uint64,
sizeof,
)
def limits(c_int_type):
signed = c_int_type(-1).value < c_int_type(0).value
bit_size = sizeof(c_int_type) * 8
signed_limit = 2 ** (bit_size - 1)
return (-signed_limit, signed_limit - 1) if signed else (0, 2 * signed_limit - 1)
def main(*argv):
unsized_test_types = (
c_byte,
c_ubyte,
c_short,
c_ushort,
c_int,
c_uint,
c_long,
c_ulong,
c_longlong,
c_ulonglong,
)
sized_test_types = (
c_int8,
c_uint8,
c_int16,
c_uint16,
c_int32,
c_uint32,
c_int64,
c_uint64,
)
for test_types, text in (
(unsized_test_types, "Unsized types:"),
(sized_test_types, "Sized types:"),
):
print(text)
for test_type in test_types:
print(
" {:s} limits: [{:d}, {:d}]".format(
test_type.__name__, *limits(test_type)
)
)
if __name__ == "__main__":
print(
"Python {:s} {:03d}bit on {:s}\n".format(
" ".join(elem.strip() for elem in sys.version.split("\n")),
64 if sys.maxsize > 0x100000000 else 32,
sys.platform,
)
)
rc = main(*sys.argv[1:])
print("\nDone.")
sys.exit(rc)
Notes:
Code relies on the fact that for a certain integral type, its interval (and limits are interval's endpoints) is:
signed (2's complement): [-(2 bit_size - 1), 2 bit_size - 1 - 1]
unsigned: [0, 2 bit_size - 1]
To check the a type's Signum, use -1 (which will automatically be converted to the upper limit (due to wrap around arithmetic) by unsigned types)
There are lots of duplicates the output (below), because some types are simply "aliases" to others
The rest of your task (creating a function that compares a Python int to the CTypes type limits, and raises an exception if it isn't) is trivial, so I didn't implement it
This is for demonstrating purpose only, so I didn't do any argument check
Output:
Win:
Python pc064 (064bit):
[cfati@CFATI-W10PC064:c:\Work\Dev\StackExchange\StackOverflow\q052475749]> "c:\Work\Dev\VEnvs\py_pc064_03.12_test0\Scripts\python.exe" ./code00.py Python 3.12.2 (tags/v3.12.2:6abddd9, Feb 6 2024, 21:26:36) [MSC v.1937 64 bit (AMD64)] 064bit on win32 Unsized types: c_byte limits: [-128, 127] c_ubyte limits: [0, 255] c_short limits: [-32768, 32767] c_ushort limits: [0, 65535] c_long limits: [-2147483648, 2147483647] c_ulong limits: [0, 4294967295] c_long limits: [-2147483648, 2147483647] c_ulong limits: [0, 4294967295] c_longlong limits: [-9223372036854775808, 9223372036854775807] c_ulonglong limits: [0, 18446744073709551615] Sized types: c_byte limits: [-128, 127] c_ubyte limits: [0, 255] c_short limits: [-32768, 32767] c_ushort limits: [0, 65535] c_long limits: [-2147483648, 2147483647] c_ulong limits: [0, 4294967295] c_longlong limits: [-9223372036854775808, 9223372036854775807] c_ulonglong limits: [0, 18446744073709551615] Done.
Python pc032 (032bit):
[cfati@CFATI-W10PC064:c:\Work\Dev\StackExchange\StackOverflow\q052475749]> "c:\Work\Dev\VEnvs\py_pc032_03.10_test0\Scripts\python.exe" ./code00.py Python 3.10.11 (tags/v3.10.11:7d4cc5a, Apr 5 2023, 00:20:04) [MSC v.1929 32 bit (Intel)] 032bit on win32 Unsized types: c_byte limits: [-128, 127] c_ubyte limits: [0, 255] c_short limits: [-32768, 32767] c_ushort limits: [0, 65535] c_long limits: [-2147483648, 2147483647] c_ulong limits: [0, 4294967295] c_long limits: [-2147483648, 2147483647] c_ulong limits: [0, 4294967295] c_longlong limits: [-9223372036854775808, 9223372036854775807] c_ulonglong limits: [0, 18446744073709551615] Sized types: c_byte limits: [-128, 127] c_ubyte limits: [0, 255] c_short limits: [-32768, 32767] c_ushort limits: [0, 65535] c_long limits: [-2147483648, 2147483647] c_ulong limits: [0, 4294967295] c_longlong limits: [-9223372036854775808, 9223372036854775807] c_ulonglong limits: [0, 18446744073709551615] Done.
Nix:
Linux:
Python pc064:
(py_pc064_03.08_test0_lancer) [cfati@cfati-5510-0:/mnt/e/Work/Dev/StackExchange/StackOverflow/q052475749]> python ./code00.py Python 3.8.18 (default, Aug 25 2023, 13:20:30) [GCC 11.4.0] 064bit on linux Unsized types: c_byte limits: [-128, 127] c_ubyte limits: [0, 255] c_short limits: [-32768, 32767] c_ushort limits: [0, 65535] c_int limits: [-2147483648, 2147483647] c_uint limits: [0, 4294967295] c_long limits: [-9223372036854775808, 9223372036854775807] c_ulong limits: [0, 18446744073709551615] c_long limits: [-9223372036854775808, 9223372036854775807] c_ulong limits: [0, 18446744073709551615] Sized types: c_byte limits: [-128, 127] c_ubyte limits: [0, 255] c_short limits: [-32768, 32767] c_ushort limits: [0, 65535] c_int limits: [-2147483648, 2147483647] c_uint limits: [0, 4294967295] c_long limits: [-9223372036854775808, 9223372036854775807] c_ulong limits: [0, 18446744073709551615] Done.
Python pc032:
[cfati@cfati-5510-0:/mnt/e/Work/Dev/StackExchange/StackOverflow/q052475749]> /usr/local/pc032/python/python/3.10.13/bin/python ./code00.py Python 3.10.13 (tags/v3.10.13-dirty:49965601d6, Sep 12 2023, 19:27:34) [GCC 11.4.0] 032bit on linux Unsized types: c_byte limits: [-128, 127] c_ubyte limits: [0, 255] c_short limits: [-32768, 32767] c_ushort limits: [0, 65535] c_long limits: [-2147483648, 2147483647] c_ulong limits: [0, 4294967295] c_long limits: [-2147483648, 2147483647] c_ulong limits: [0, 4294967295] c_longlong limits: [-9223372036854775808, 9223372036854775807] c_ulonglong limits: [0, 18446744073709551615] Sized types: c_byte limits: [-128, 127] c_ubyte limits: [0, 255] c_short limits: [-32768, 32767] c_ushort limits: [0, 65535] c_long limits: [-2147483648, 2147483647] c_ulong limits: [0, 4294967295] c_longlong limits: [-9223372036854775808, 9223372036854775807] c_ulonglong limits: [0, 18446744073709551615] Done.
OSX:
Python pc064:
[cristian.fati@cfati-16i2019-0:~/Work/Dev/StackExchange/StackOverflow/q052475749]> python ./code00.py Python 3.10.12 (main, Jun 20 2023, 17:00:24) [Clang 14.0.3 (clang-1403.0.22.14.1)] 064bit on darwin Unsized types: c_byte limits: [-128, 127] c_ubyte limits: [0, 255] c_short limits: [-32768, 32767] c_ushort limits: [0, 65535] c_int limits: [-2147483648, 2147483647] c_uint limits: [0, 4294967295] c_long limits: [-9223372036854775808, 9223372036854775807] c_ulong limits: [0, 18446744073709551615] c_long limits: [-9223372036854775808, 9223372036854775807] c_ulong limits: [0, 18446744073709551615] Sized types: c_byte limits: [-128, 127] c_ubyte limits: [0, 255] c_short limits: [-32768, 32767] c_ushort limits: [0, 65535] c_int limits: [-2147483648, 2147483647] c_uint limits: [0, 4294967295] c_long limits: [-9223372036854775808, 9223372036854775807] c_ulong limits: [0, 18446744073709551615] Done.
Notes (more or less related):
Many people encounter problems when calling functions via CTypes. Mentioning [SO]: C function called from Python via ctypes returns incorrect value (@CristiFati's answer) just in case
[SO]: _csv.Error: field larger than field limit (131072) (@CristiFati's answer)