I'm currently struggling with python's bit operations as in python3 there is no difference anymore between 32bit integers (int) and 64bit integers (long).
What I want is an efficient function that takes any integer and cuts the most significant 32 bits and then converts these 32 bits back to an integer with the correct sign.
Example 1:
>>> bin_string = bin(3293670138 & 0b11111111111111111111111111111111)
>>> print(bin_string)
0b11000100010100010110101011111010
>>> print(int(bin_string,2))
3293670138
But the result should have been -1001297158 since converting '11000100010100010110101011111010' in a 32 bits integer is a negative number.
I already have my own solution:
def int32(val):
if val >= -2**31 and val <= 2**31-1:
return val
bin_string = bin(val & 0b11111111111111111111111111111111)
int_val = int(bin_string,2)
if int_val > 2147483647:
return -(~(int_val-1)%2**32)
return int_val
However, I would like to know if someone has a more elegant and performant idea.
Thank you in advance!
Significantly simpler solution: Let ctypes
do the work for you.
from ctypes import c_int32
def int32(val):
return c_int32(val).value
That just constructs a c_int32
type from the provided Python int
, which truncates as you desire, then extracts the value back out as a normal Python int
type. The time taken is less than that of your existing function for values that actually require trimming. On my machine, it takes about 155-175 ns reliably, where your function is more variable, taking around 310-320 ns for positive values, and 405-415 ns for negative values.
It is slower for values that don't require trimming though; your code excludes them relatively cheaply (I improved it slightly by changing the test to if -2 ** 31 <= val < 2 ** 31:
), taking ~75 ns, but this code does the conversion no matter what, taking the same roughly fixed amount of time. If numbers usually fit, and performance is critical, you can short cut out the way your original code (with slightly modification from me) did:
def int32(val):
if -2 ** 31 <= val < 2 ** 31:
return val
return c_int32(val).value
That makes the "must truncate" case slightly slower (just under 200 ns) in exchange for making the "no truncation needed" case faster (below 80 ns).
Importantly, either way, it's fairly simple. It doesn't involve maintaining complicated code, and it's largely self-documenting; you tell it to make a signed 32 bit int, and it does so.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With