16

I was trying to port a function from C to Python and to make it easy to debug, I'd prefer it performed the same CPU word-size limited operations so I could compare the intermediate results. In other words, I'd like something like:

a = UnsignedBoundedInt(32, 399999)
b = UnsignedBoundedInt(32, 399999)
print(a*b) # prints 1085410049 (159999200001 % 2**32)

What's the best way to achieve this so that all operations (including bitwise shifts) would work as in C?

d33tah
  • 10,055
  • 12
  • 60
  • 142

2 Answers2

23

You can try using ctypes.uint_32 to bound the results for you:

>>> import ctypes
>>> print ctypes.c_uint32(399999 * 399999).value
1085410049

Alternatively you can use numpy's data types:

>>> import numpy as np
>>> a = np.uint32(399999)
>>> b = np.uint32(399999)
>>> a * b
__main__:1: RuntimeWarning: overflow encountered in uint_scalars
1085410049
Claudiu
  • 216,039
  • 159
  • 467
  • 667
  • v1 -= ((v0 << 4 ^ v0 >> 5) + v0) ^ (sum + k[sum>>11 & 3]) TypeError: unsupported operand type(s) for <<: and=""> – d33tah Oct 07 '13 at 19:47
  • And: >>> type(numpy.uint32(3) << 4) – d33tah Oct 07 '13 at 19:54
  • TypeError: unsupported operand type(s) for <<: and=""> – d33tah Oct 07 '13 at 19:57
  • So, looks like it's not exactly the best solution. – d33tah Oct 07 '13 at 19:57
  • @d33tah You forgot about the `.value` in the `ctypes` case. Besides, you should put this "clipping operation" one level towards the outside. That is, if you have `clip_u32 = lambda val: ctypes.c_uint32(val).value`, you could do `v1 -= clip_u32((v0 << 4 ^ v0 >> 5) + v0) ^ clip_u32(sum + k[sum>>11 & 3])`. – glglgl Oct 07 '13 at 20:36
  • @d33tah: With the `ctypes` approach you have to always convert to py ints and convert back for each op. Not the nicest approach, true. `numpy` does more what you want. Alternatively you can subclass int yourself and override all the ops to do the ctypes conversion & back – Claudiu Oct 07 '13 at 22:35
3

Here's an interesting solution, though it only works under Python 2:

class U32:
    """Emulates 32-bit unsigned int known from C programming language."""

    def __init__(self, num=0, base=None):
        """Creates the U32 object.

        Args:
            num: the integer/string to use as the initial state
            base: the base of the integer use if the num given was a string
        """
        if base is None:
            self.int_ = int(num) % 2**32
        else:
            self.int_ = int(num, base) % 2**32

    def __coerce__(self, ignored):
        return None

    def __str__(self):
        return "<U32 instance at 0x%x, int=%d>" % (id(self), self.int_)

    def __getattr__(self, attribute_name):
        print("getattr called, attribute_name=%s" % attribute_name)
        # you might want to take a look here:
        # https://stackoverflow.com/q/19611001/1091116
        r = getattr(self.int_, attribute_name)
        if callable(r):  # return a wrapper if integer's function was requested
            def f(*args, **kwargs):
                if args and isinstance(args[0], U32):
                    args = (args[0].int_, ) + args[1:]
                ret = r(*args, **kwargs)
                if ret is NotImplemented:
                    return ret
                if attribute_name in ['__str__', '__repr__', '__index__']:
                    return ret
                ret %= 2**32
                return U32(ret)
            return f
        return r

print(U32(4) / 2)
print(4 / U32(2))
print(U32(4) / U32(2))

For Python 3 compatibility, have a look here.

Community
  • 1
  • 1
d33tah
  • 10,055
  • 12
  • 60
  • 142