3

Is it possible to somehow override or overload the standard implementation of ints/numbers in python so that it acts like a 32-bit int.

a: int
a = 4076863488
>>> -218103808

Or is it possible to somehow define a variable that can't change type? Doing something like: x: int? I want to do this because it's annoying to write ctypes.c_int32(n) on every bit operation and assignment. Especially since Python does not use 32 bits bitwise operands.

I know I'm basically trying to change the nature of the language. So maybe I'm asking what you would do if you had to do 32-bit stuff in python.

martineau
  • 119,623
  • 25
  • 170
  • 301
objectnabb
  • 93
  • 2
  • 9
  • 1
    Changing the standard implementation or fixing type for a variable isn't possible (without heavily modifying Python's source code). Most bit operations (`& | ^`) work with Python's `int` very well without much difference to an `int32`. – Michael Butscher Nov 03 '18 at 23:50
  • 1
    `int`s in Python itself are variable length, but as @MichaelButscher correctly points out, you can generally ignore that and just don't put anything in one that requires more than 32-bits to represent. – martineau Nov 03 '18 at 23:55
  • 1
    so.. numpy or ctypes then maybe. It needs to overflow, im implementing c code to python (read trying) – objectnabb Nov 03 '18 at 23:55
  • 1
    The [`array`](https://docs.python.org/3/library/array.html#module-array) and [`struct`](https://docs.python.org/3/library/struct.html#module-struct) modules may also be helpful, depending on exactly what you're doing. Also see the answers to the question [python 32-bit and 64-bit integer math with intentional overflow](https://stackoverflow.com/questions/16745387/python-32-bit-and-64-bit-integer-math-with-intentional-overflow). – martineau Nov 03 '18 at 23:58

1 Answers1

7

Some options:

  • Use Cython. You can declare a native 32-bit int type there, and you even get the advantage that pure numerical code gets compiled to (very) fast C code.
  • Use a numpy array of a single element: np.zeros((1,), dtype=np.int32). Provided you only ever use in-place operations (+=, *=, etc.), this will work like a 32-bit int type. Do be aware that if you ever use a regular binary operator (e.g. myint + 3), you might be subjected to type promotion or conversion, and the result will no longer be int32.
  • Use ctypes.c_int32. This comes built-in to Python, but supports no mathematical operations so you have to wrap and unwrap yourself (e.g. newval = c_int32(v1.value + v2.value)).
  • Use a library like fixedint (shameless plug), which provides fixed-integer classes that remain fixed size through operations rather than decaying to int. fixedint was specifically designed with fixed-width bitwise math in mind. In this case you would use fixedint.Int32.

Some less desirable options:

  • struct: Throws errors if your input is out of range. You can work around this with unpack('i', pack('I', val & 0xffffffff))[0], but that's really unwieldy.
  • array: Throws errors if you try to store a value out of range. Harder to work around than struct.
  • Manual bitmashing. With an unsigned 32-bit int, this is just a matter of adding & 0xffffffff a lot, which is not too bad. But, Python doesn't have any built-in way to wrap a value to a signed 32-bit int, so you'll have to write your own int32 conversion function and wrap all your operations with it:

    def to_int32(val):
        val &= ((1<<32)-1)
        if val & (1<<31): val -= (1<<32)
        return val
    

Demonstrations of your options:

Cython

cpdef int munge(int val):
    cdef int x
    x = val * 32
    x += 0x7fffffff
    return x

Save as int_test.pyx and compile with cythonize -a -i int_test.pyx.

>>> import int_test
>>> int_test.munge(3)
-2147483553

NumPy

import numpy as np

def munge(val):
    x = val.copy()
    x *= 32
    x += 0x7fffffff
    return x

def to_int32(val):
    return np.array((val,), dtype=np.int32)

print(munge(to_int32(3)))
# prints [-2147483553]

ctypes

from ctypes import c_int32
def munge(val):
    x = c_int32(val.value * 32)
    x = c_int32(x.value + 0x7fffffff)
    return x

print(munge(c_int32(3)))
# prints c_int(-2147483553)

fixedint

import fixedint

def munge(val):
    x = val * 32
    x += 0x7fffffff
    return x

print(munge(fixedint.Int32(3)))
# prints -2147483553
nneonneo
  • 171,345
  • 36
  • 312
  • 383
  • Good list; you should add `ctypes` as it is available basically everywhere without installing anything extra. – John Zwinck Nov 04 '18 at 00:02
  • objectnabb: `fixedint` is also available from [PyPi](https://pypi.org/project/fixedint/) which means it can easily be installed via `pip`. – martineau Nov 04 '18 at 00:06
  • Thank you! This is a very good list. I will have to check these out. – objectnabb Nov 04 '18 at 00:22
  • I am a little bit confuse here. ` b = 4076863488 print(b) print(ctypes.c_int32(b)) print(b & 0xffffffff) ` outputs: `4076863488 c_long(-218103808) 4076863488 ` – objectnabb Nov 04 '18 at 00:49
  • @objectnabb `b` is unchanged by the call to `c_int32` - the middle print statement is printing a new object that has the signed 32-bit value of the original. Because `b` has bit 31 set, it is interpreted as a negative integer in 32 bit 2s complement. – nneonneo Nov 04 '18 at 01:17