Python的两个补充

Jim*_*Jim 58 python bit-manipulation twos-complement

python中是否有内置函数将二进制字符串(例如'111111111111')转换为二进制补码整数 -1?

tra*_*avc 65

(1<<bits)如果最高位为1 ,则二进制补码减去.例如,以8位为例,这给出了127到-128的范围.

两个补码的函数...

def twos_comp(val, bits):
    """compute the 2's complement of int value val"""
    if (val & (1 << (bits - 1))) != 0: # if sign bit is set e.g., 8bit: 128-255
        val = val - (1 << bits)        # compute negative value
    return val                         # return positive value as is
Run Code Online (Sandbox Code Playgroud)

从二进制字符串开始特别容易......

binary_string = '1111' # or whatever... no '0b' prefix
out = twos_comp(int(binary_string,2), len(binary_string))
Run Code Online (Sandbox Code Playgroud)

对我来说更有用的是从十六进制值(本例中为32位)...

hex_string = '0xFFFFFFFF' # or whatever... '0x' prefix doesn't matter
out = twos_comp(int(hex_string,16), 32)
Run Code Online (Sandbox Code Playgroud)


Sco*_*ths 20

它不是内置的,但如果你想要不寻常的长度数,那么你可以使用bitstring模块.

>>> from bitstring import Bits
>>> a = Bits(bin='111111111111')
>>> a.int
-1
Run Code Online (Sandbox Code Playgroud)

可以通过多种方式等效地创建相同的对象,包括

>>> b = Bits(int=-1, length=12)
Run Code Online (Sandbox Code Playgroud)

它只是一个任意长度的字符串,并使用属性来获得不同的解释:

>>> print a.int, a.uint, a.bin, a.hex, a.oct
-1 4095 111111111111 fff 7777
Run Code Online (Sandbox Code Playgroud)

  • @ erikb85:那里的答案(包括我的)并没有真正进入界面的简单性和灵活性(OP甚至开始抱怨'bitarray`比他需要的更多......),所以这些问题是一个很好的补充彼此:这个显示了像`bitstring`这样的库如何使常见的操作更容易编写,一个显示它们不会使它们更快,并且经常使它们变慢. (2认同)

Gaë*_*ard 10

从Python 3.2开始,有用于字节操作的内置函数:https://docs.python.org/3.4/library/stdtypes.html#int.to_bytes.

通过组合to_bytes和from_bytes,你得到了

def twos(val_str, bytes):
    import sys
    val = int(val_str, 2)
    b = val.to_bytes(bytes, byteorder=sys.byteorder, signed=False)                                                          
    return int.from_bytes(b, byteorder=sys.byteorder, signed=True)
Run Code Online (Sandbox Code Playgroud)

校验:

twos('11111111', 1)  # gives -1
twos('01111111', 1)  # gives 127
Run Code Online (Sandbox Code Playgroud)

对于旧版本的Python,travc的答案很好但如果想要使用整数而不是字符串,它对负值不起作用.对于每个val,f(f(val))== val为真的二进制补码函数是:

def twos_complement(val, nbits):
    """Compute the 2's complement of int value val"""
    if val < 0:
        val = (1 << nbits) + val
    else:
        if (val & (1 << (nbits - 1))) != 0:
            # If sign bit is set.
            # compute negative value.
            val = val - (1 << nbits)
    return val
Run Code Online (Sandbox Code Playgroud)


Joh*_*ooy 8

>>> bits_in_word=12
>>> int('111111111111',2)-(1<<bits_in_word)
-1
Run Code Online (Sandbox Code Playgroud)

这是因为:

二进制数的二进制补码定义为通过从2的大次幂中减去该数得到的值(具体地,对于N位二进制补码,从2 ^ N减去).数字的二进制补码在大多数算术中表现得像原始数的负数,并且它可以以自然的方式与正数共存.

  • @quqa123在应用此方法之前,必须首先检查该值是否为负数。如果该值小于或等于“(1 &lt;&lt; (bits_in_word - 1)) - 1”,则为正数,不应使用此方法。 (2认同)

Tom*_*tyn 7

这将使用按位逻辑有效地为您提供二进制补码:

def twos_complement(value, bitWidth):
    if value >= 2**bitWidth:
        # This catches when someone tries to give a value that is out of range
        raise ValueError("Value: {} out of range of {}-bit value.".format(value, bitWidth))
    else:
        return value - int((value << 1) & 2**bitWidth)
Run Code Online (Sandbox Code Playgroud)

这个怎么运作:

首先,我们确保用户向我们传递了一个在所提供位范围内的值(例如,有人给了我们 0xFFFF 并指定了 8 位)该问题的另一种解决方案是按位 AND (&) 将值与(2**位宽)-1

为了得到结果,该值向左移动 1 位。这将值(符号位)的 MSB 移动到要与 和的位置2**bitWidth。当符号位为“0”时,减数变为 0,结果为value - 0。当符号位为“1”时,减数变为2**bitWidth,结果为value - 2**bitWidth

示例 1:如果参数为 value=0xFF (255d, b11111111) 和 bitWidth=8

  1. 0xFF - int((0xFF << 1) & 2**8)
  2. 0xFF - int((0x1FE) & 0x100)
  3. 0xFF - 整数(0x100)
  4. 255 - 256
  5. -1

示例 2:如果参数为 value=0x1F (31d, b11111) 和 bitWidth=6

  1. 0x1F - int((0x1F << 1) & 2**6)
  2. 0x1F - int((0x3E) & 0x40)
  3. 0x1F - 整数(0x00)
  4. 31 - 0
  5. 31

示例 3:值 = 0x80,位宽 = 7

ValueError: Value: 128 out of range of 7-bit value.

示例 4:值 = 0x80,bitWitdh = 8

  1. 0x80 - int((0x80 << 1) & 2**8)
  2. 0x80 - int((0x100) & 0x100)
  3. 0x80 - 整数(0x100)
  4. 128 - 256
  5. -128

现在,使用其他人已经发布的内容,将您的位串传递到 int(bitstring,2) 并传递给 twos_complement 方法的 value 参数。


jfs*_*jfs 4

几个实现(仅作为说明,不用于使用):

def to_int(bin):
    x = int(bin, 2)
    if bin[0] == '1': # "sign bit", big-endian
       x -= 2**len(bin)
    return x

def to_int(bin): # from definition
    n = 0
    for i, b in enumerate(reversed(bin)):
        if b == '1':
           if i != (len(bin)-1):
              n += 2**i
           else: # MSB
              n -= 2**i 
    return n
Run Code Online (Sandbox Code Playgroud)