如何在Python中实现逻辑右移?

45

如标题所示,在JavaScript中有一个特定的运算符>>>。例如,在JavaScript中我们将得到以下结果:

(-1000) >>> 3 = 536870787

(-1000) >> 3 = -125

1000 >>> 3 = 125

1000 >> 3 = 125

那么是否有某种方法或运算符表示这个 >>>

12个回答

60

没有一个内置的运算符可以完成这个功能,但你可以很容易地模拟 >>> 的操作:

>>> def rshift(val, n): return val>>n if val >= 0 else (val+0x100000000)>>n
... 
>>> rshift(-1000, 3)
536870787
>>> rshift(1000, 3)
125
以下另一种实现方式可以避免使用 if 语句:
>>> def rshift(val, n): return (val % 0x100000000) >> n

4
(val + 0x100000000) % 0x100000000) 等同于 val % 0x100000000 - Baffe Boyois
1
@Baffe Boyois:在Python中,它绝对不是这样的(结果始终为正数,而第一个操作数可以有任何符号)。 - NPE
3
例子?请注意,在Python中,-1 % 3 == 2"模运算符始终生成一个与其第二个操作数(或零)具有相同符号的结果" - Baffe Boyois
9
0x100000000 是 1 << 32,其中的32是用于表示整数的位数(这一点很重要但没有明确说明)。如果你使用64位整数,则需要使用 1 << 64 - Scott Griffiths
@systemBuilder 这是其他语言中逻辑/无符号右移的行为方式。如果您执行 val >>> 0,它基本上会将其转换为无符号。打开您的浏览器控制台并自行尝试。 - Inkling
显示剩余2条评论

11

不,Python中没有逻辑右移运算符。Python中的右移运算符是算术右移。


1
这个东西无论如何都包含在某个软件包中吗? - dementrock
由于Python没有无符号数,因此它不包括无符号移位是有道理的。 - Mark Ransom
3
@Mark Ransom:那是错误的,你可能需要计算另一种语言中定义的表达式(就像我现在所做的)。但我同意,由于不存在无符号数,因此对无符号移位的需求相当有限。 - maaartinus

3
这是aix的回答的一个分支。如果您输入一个正值,普通的右移运算符将起作用,因此您实际上正在寻找从有符号到无符号的转换。
def unsigned32(signed):
    return signed % 0x100000000

>>> unsigned32(-1000) >> 3
536870787L

2

Numpy 提供了 right_shift() 函数来实现这个功能:

>>> import numpy
>>> numpy.right_shift(1000, 3)
125

11
但这不就是 >> 运算符吗?如果我尝试 numpy.right_shift(-1000, 3),我得到的是 -125 而不是 536870787 - Scott Griffiths
@ScottGriffiths 是正确的。但是你可以使用numpy将其转换为无符号类型,然后正常运算符就可以工作了,例如:numpy.uint32(-1000) >> 3。请注意,当我测试这个特定情况时,输出类型是numpy.int64(有符号)。 - Inkling

2
使用bitstring模块的>>=运算符,您可以对位移进行零填充。请参考bitstring模块的>>=操作符。请保留HTML标签。
>>> a = BitArray(int=-1000, length=32)
>>> a.int
-1000
>>> a >>= 3
>>> a.int
536870787

旋转不同于移位。 - Mark Ransom
@Mark:说得好 - 我误解了 >>> 运算符的作用。现在已经修复了。 - Scott Griffiths

2
尝试通过使用0x100000000掩码翻转负数的符号位是基本上存在误解的,因为它对字长做出了硬性假设。在我作为程序员的时间里,我曾经使用过24、48、16、18、32、36和64位数字。我还听说过一些机器使用奇数长度,如37和其他使用补码而不是二进制补码算术的机器。任何你对数字内部表示所做的假设都是危险的,除了它们是二进制的这一点。

即使是二进制假设也并非绝对安全,但我认为我们可以接受。


1
你需要记住,如果数字是负数,那么最高位被设置了,在每次右移时,你需要保持最高位的设置。

这是我的实现:

def rshift(val, n):
    s = val & 0x80000000
    for i in range(0,n):
        val >>= 1
        val |= s
    return val

我认为你误解了问题的意思。所有的Python移位都是算术移位。你已经实现了一个算术移位,复制了Python的功能。原始问题要求的是逻辑移位运算符,而不是算术移位。 - systemBuilder

0

你也可以使用地板除法:

def rshift(val, n):
    if val > 0:
        return val >> n
    return val // -(2^n)

0
一个不需要使用模运算的解决方案: >>> def rshift(val,n): return (val>>n) & (0x7fffffff>>(n-1)) 这个方法有效是因为7fffffff是一个正数,右移时会在左边添加零。

0

这不是一种高效的方法,但它能够按预期工作。

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

参考资料:

https://open4tech.com/logical-vs-arithmetic-shift/

https://www.interviewcake.com/concept/java/bit-shift


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接