Search code examples
pythonintegermaxsizectypes

Maximum and minimum value of C types integers from Python


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.


Solution

  • 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):