How to get the logical right binary shift in python
Question:
As revealed by the title, in JavaScript there is a specific operator >>>
. For example, in JavaScript we will have the following result:
(-1000) >>> 3 = 536870787
(-1000) >> 3 = -125
1000 >>> 3 = 125
1000 >> 3 = 125
So is there a certain method or operator representing this >>>
?
Answers:
No, there isn’t. The right shift in python is arithmetical.
There isn’t a built-in operator for this, but you can easily simulate the >>>
yourself:
>>> def rshift(val, n): return val>>n if val >= 0 else (val+0x100000000)>>n
...
>>> rshift(-1000, 3)
536870787
>>> rshift(1000, 3)
125
The following alternative implementation removes the need for the if
:
>>> def rshift(val, n): return (val % 0x100000000) >> n
Numpy provides the right_shift()
function that does this:
>>> import numpy
>>> numpy.right_shift(1000, 3)
125
Here’s a spinoff of aix’s answer. The normal right-shift operator will work if you feed it a positive value, so you’re really looking for a conversion from signed to unsigned.
def unsigned32(signed):
return signed % 0x100000000
>>> unsigned32(-1000) >> 3
536870787L
Trying to flip the sign bit of a negative number by masking it with 0x100000000 is fundamentally misconceived as it makes hard assumptions about the word length. In my time as a programmer I have worked with 24-, 48-, 16-, 18-, 32-, 36- and 64-bit numbers. I have also heard of machines that work on odd-lengths, such as 37 and others that use ones-complement, and not twos-complement, arithmetic. Any assumptions you make about the internal representation of numbers, beyond that they are binary, is dangerous.
Even the binary assumption is not absolutely safe, but I think we’ll allow that. 🙂
You need to remember that if the number is negative, the top bit is set and with each shift right you need to make the top bit set as well.
Here is my implementation:
def rshift(val, n):
s = val & 0x80000000
for i in range(0,n):
val >>= 1
val |= s
return val
A solution that works without a modulo:
>>> def rshift(val,n): return (val>>n) & (0x7fffffff>>(n-1))
This works since 7fffffff is a positive number and right shifting that will add zeros to the left.
You could also use floor division:
def rshift(val, n):
if val > 0:
return val >> n
return val // -(2^n)
This is not an efficient approach but works just as expected
def _toBinary(x):
x=int(x)
binary = []
while x > 0:
binary.append(str(x%2))
x=int(x/2)
return "".join(binary[::-1])
def _fromBinary(xs):
ans = 0
for i,x in enumerate(xs[::-1]):
if x == '1':
ans += 2**i
return ans
def leftLogicalShift(x,n=1):
if not type(x) == int:
return x
xs = _toBinary(x)
xs = [x for x in xs]
for _ in range(n):
xs.pop(0)
xs.append('0')
return _fromBinary("".join(xs))
def rightLogicalShift(x,n=1):
if not type(x) == int:
return x
xs = _toBinary(x)
xs = [x for x in xs]
for _ in range(n):
xs.pop()
xs.insert(0,'0')
return _fromBinary("".join(xs))
def leftArithmeticShift(x,n=1):
return leftLogicalShift(x,n)
def rightArithmeticShift(x,n=1):
if not type(x) == int:
return x
xs = _toBinary(x)
xs = [x for x in xs]
for _ in range(n):
tmp = xs[0]
xs.pop()
xs.insert(0,tmp)
return _fromBinary("".join(xs))
lls = leftLogicalShift(10,2)
print(lls) # 8
rls = rightLogicalShift(10,2)
print(rls) # 2
las = leftArithmeticShift(10,2)
print(las) # 8
ras = rightArithmeticShift(10,2)
print(ras) # 14
references:
I think a logical right binary shift is not available in Python directly. Instead you can use Javascript in Python as in:
import js2py
rshift = js2py.eval_js('function $(a, b){ return a >>> b}')
print (rshift(244, 324)) #15
The top-voted answer produces WRONG results for val < 0 and n == 0!
def rshift(val, n):
if (val >= 0): return val >> n
elif (n == 0): return val
else: return (val + 0x10000000) >> n
>>> rshift(-1, 0)
-1
As revealed by the title, in JavaScript there is a specific operator >>>
. For example, in JavaScript we will have the following result:
(-1000) >>> 3 = 536870787
(-1000) >> 3 = -125
1000 >>> 3 = 125
1000 >> 3 = 125
So is there a certain method or operator representing this >>>
?
No, there isn’t. The right shift in python is arithmetical.
There isn’t a built-in operator for this, but you can easily simulate the >>>
yourself:
>>> def rshift(val, n): return val>>n if val >= 0 else (val+0x100000000)>>n
...
>>> rshift(-1000, 3)
536870787
>>> rshift(1000, 3)
125
The following alternative implementation removes the need for the if
:
>>> def rshift(val, n): return (val % 0x100000000) >> n
Numpy provides the right_shift()
function that does this:
>>> import numpy
>>> numpy.right_shift(1000, 3)
125
Here’s a spinoff of aix’s answer. The normal right-shift operator will work if you feed it a positive value, so you’re really looking for a conversion from signed to unsigned.
def unsigned32(signed):
return signed % 0x100000000
>>> unsigned32(-1000) >> 3
536870787L
Trying to flip the sign bit of a negative number by masking it with 0x100000000 is fundamentally misconceived as it makes hard assumptions about the word length. In my time as a programmer I have worked with 24-, 48-, 16-, 18-, 32-, 36- and 64-bit numbers. I have also heard of machines that work on odd-lengths, such as 37 and others that use ones-complement, and not twos-complement, arithmetic. Any assumptions you make about the internal representation of numbers, beyond that they are binary, is dangerous.
Even the binary assumption is not absolutely safe, but I think we’ll allow that. 🙂
You need to remember that if the number is negative, the top bit is set and with each shift right you need to make the top bit set as well.
Here is my implementation:
def rshift(val, n):
s = val & 0x80000000
for i in range(0,n):
val >>= 1
val |= s
return val
A solution that works without a modulo:
>>> def rshift(val,n): return (val>>n) & (0x7fffffff>>(n-1))
This works since 7fffffff is a positive number and right shifting that will add zeros to the left.
You could also use floor division:
def rshift(val, n):
if val > 0:
return val >> n
return val // -(2^n)
This is not an efficient approach but works just as expected
def _toBinary(x):
x=int(x)
binary = []
while x > 0:
binary.append(str(x%2))
x=int(x/2)
return "".join(binary[::-1])
def _fromBinary(xs):
ans = 0
for i,x in enumerate(xs[::-1]):
if x == '1':
ans += 2**i
return ans
def leftLogicalShift(x,n=1):
if not type(x) == int:
return x
xs = _toBinary(x)
xs = [x for x in xs]
for _ in range(n):
xs.pop(0)
xs.append('0')
return _fromBinary("".join(xs))
def rightLogicalShift(x,n=1):
if not type(x) == int:
return x
xs = _toBinary(x)
xs = [x for x in xs]
for _ in range(n):
xs.pop()
xs.insert(0,'0')
return _fromBinary("".join(xs))
def leftArithmeticShift(x,n=1):
return leftLogicalShift(x,n)
def rightArithmeticShift(x,n=1):
if not type(x) == int:
return x
xs = _toBinary(x)
xs = [x for x in xs]
for _ in range(n):
tmp = xs[0]
xs.pop()
xs.insert(0,tmp)
return _fromBinary("".join(xs))
lls = leftLogicalShift(10,2)
print(lls) # 8
rls = rightLogicalShift(10,2)
print(rls) # 2
las = leftArithmeticShift(10,2)
print(las) # 8
ras = rightArithmeticShift(10,2)
print(ras) # 14
references:
I think a logical right binary shift is not available in Python directly. Instead you can use Javascript in Python as in:
import js2py
rshift = js2py.eval_js('function $(a, b){ return a >>> b}')
print (rshift(244, 324)) #15
The top-voted answer produces WRONG results for val < 0 and n == 0!
def rshift(val, n):
if (val >= 0): return val >> n
elif (n == 0): return val
else: return (val + 0x10000000) >> n
>>> rshift(-1, 0)
-1