Python的两个补充

时间:2009-10-22 00:53:13

标签: python bit-manipulation twos-complement

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

17 个答案:

答案 0 :(得分:63)

如果最高位为1,则二进制补码减去(1<<bits)。例如,以8位为单位,这将得到127到-128的范围。

一个int的两个补码的函数...

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

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

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

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

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

答案 1 :(得分:20)

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

>>> from bitstring import Bits
>>> a = Bits(bin='111111111111')
>>> a.int
-1

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

>>> b = Bits(int=-1, length=12)

它的行为类似于任意长度的字符串,并使用属性来获得不同的解释:

>>> print a.int, a.uint, a.bin, a.hex, a.oct
-1 4095 111111111111 fff 7777

答案 2 :(得分: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)

检查:

twos('11111111', 1)  # gives -1
twos('01111111', 1)  # gives 127

对于旧版本的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

答案 3 :(得分:9)

>>> bits_in_word=12
>>> int('111111111111',2)-(1<<bits_in_word)
-1

这是因为:

  

二进制的二进制补码   number定义为值   通过减去数字得到   从两个大的力量   (具体地,对于N比特,从2 ^ N开始   二的补充)。两个人   然后,数字的补码表现出来   喜欢原作的否定   大多数算术中的数字,它可以   与a中的正数共存   自然的方式。

答案 4 :(得分:3)

这将使用按位逻辑有效地为您提供两个补码:

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)

工作原理:

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

的值

要获得结果,该值向左移1位。这会将值的MSB(符号位)移动到与2**bitWidth相关的位置。当符号位为&#39; 0&#39;减数变为0,结果为value - 0。当符号位为&#39; 1&#39;减数变为2**bitWidth,结果为value - 2**bitWidth

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

  1. 0xFF - int((0xFF&lt;&lt;&lt; 1)&amp; 2 ** 8)
  2. 0xFF - int((0x1FE)&amp; 0x100)
  3. 0xFF - int(0x100)
  4. 255 - 256
  5. -1
  6. 示例2:如果参数值为= 0x1F(31d,b11111)且bitWidth = 6

    1. 0x1F - int((0x1F <&lt;&lt; 1)&amp; 2 ** 6)
    2. 0x1F - int((0x3E)&amp; 0x40)
    3. 0x1F - int(0x00)
    4. 31 - 0
    5. 31
    6. 示例3:value = 0x80,bitWidth = 7

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

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

      1. 0x80 - int((0x80&lt;&lt; 1)&amp; 2 ** 8)
      2. 0x80 - int((0x100)&amp; 0x100)
      3. 0x80 - int(0x100)
      4. 128 - 256
      5. -128
      6. 现在,使用其他人已发布的内容,将您的bitstring传递给int(bitstring,2)并传递给twos_complement方法的值参数。

答案 5 :(得分:3)

一些实现(仅用于说明,不打算使用):

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

答案 6 :(得分:2)

不,没有内置函数可以将two's complement二进制字符串转换为小数。

执行此操作的简单用户定义函数:

textbox

请注意,此函数不会将位宽作为参数,而是必须使用一个或多个前导零位指定正输入值。

示例:

def two2dec(s):
  if s[0] == '1':
    return -1 * (int(''.join('1' if x == '0' else '0' for x in s), 2) + 1)
  else:
    return int(s, 2)

答案 7 :(得分:1)

由于erikb85带来了性能,这里travc's answer针对Scott Griffiths'

In [534]: a = [0b111111111111, 0b100000000000, 0b1, 0] * 1000
In [535]: %timeit [twos_comp(x, 12) for x in a]
100 loops, best of 3: 8.8 ms per loop
In [536]: %timeit [bitstring.Bits(uint=x, length=12).int for x in a]
10 loops, best of 3: 55.9 ms per loop

因此,在the other question中,bitstring几乎比int慢一个数量级。但另一方面,很难超越简单性 - 我将uint转换为位串然后转换为int;你必须努力而不是来理解这一点,或者找到任何可以引入bug的地方。正如Scott Griffiths的回答所暗示的那样,对于同一个应用程序来说,这个类可以有更多的灵活性。但是第三方面,travc的回答清楚地说明了实际发生了什么 - 即使是新手应该能够理解从unsigned int到2s补码signed int的转换只意味着阅读2行代码。

无论如何,与另一个关于直接操作位的问题不同,这个问题就是关于固定长度的int算术,只是奇怪的大小。所以我猜你是否需要性能,这可能是因为你有很多这些东西,所以你可能希望它被矢量化。调整travc对numpy的回答:

def twos_comp_np(vals, bits):
    """compute the 2's compliment of array of int values vals"""
    vals[vals & (1<<(bits-1)) != 0] -= (1<<bits)
    return vals

现在:

In [543]: a = np.array(a)
In [544]: %timeit twos_comp_np(a.copy(), 12)
10000 loops, best of 3: 63.5 µs per loop

您可能可以通过自定义C代码击败它,但您可能不必这样做。

答案 8 :(得分:1)

如果有人也需要反方向:

def num_to_bin(num, wordsize):
    if num < 0:
        num = 2**wordsize+num
    base = bin(num)[2:]
    padding_size = wordsize - len(base)
    return '0' * padding_size + base

for i in range(7, -9, -1):
    print num_to_bin(i, 4)

应输出: 0111 0110 0101 0100 0011 0010 0001 0000 1111 1110 1101 1100 1011 1010 1001 1000

答案 9 :(得分:0)

我正在使用Python 3.4.0

在Python 3中,我们遇到了数据类型转换的一些问题。

所以......在这里,我会告诉小费(就像我一样),它们对十六进制字符串有很多作用。

我将采用十六进制数据并对其进行补充:

a = b'acad0109'

compl = int(a,16)-pow(2,32)

result=hex(compl)
print(result)
print(int(result,16))
print(bin(int(result,16)))

result = -1397948151或-0x5352fef7或&#39; -0b1010011010100101111111011110111&#39;

答案 10 :(得分:0)

不幸的是,没有内置函数将无符号整数转换为二进制补码,但是我们可以使用按位运算来定义一个函数:

def s12(value):
    return -(value & 0b100000000000) | (value & 0b011111111111)

第一个按位和操作用于对负数进行符号扩展(最高有效位设置),而第二个用于获取剩余的11位。这是有效的,因为Python中的整数被视为任意精度的二进制补码值。

然后,您可以将其与int函数结合使用,将二进制数字字符串转换为无符号整数形式,然后将其解释为12位有符号值。

>>> s12(int('111111111111', 2))
-1
>>> s12(int('011111111111', 2))
2047
>>> s12(int('100000000000', 2))
-2048

此函数的一个不错的属性是它的幂等性,因此已签名值的值不会改变。

>>> s12(-1)
-1

答案 11 :(得分:0)

这适用于3个字节。 Live code is here

def twos_compliment(byte_arr):
   a = byte_arr[0]; b = byte_arr[1]; c = byte_arr[2]
   out = ((a<<16)&0xff0000) | ((b<<8)&0xff00) | (c&0xff)
   neg = (a & (1<<7) != 0)  # first bit of a is the "signed bit." if it's a 1, then the value is negative
   if neg: out -= (1 << 24)
   print(hex(a), hex(b), hex(c), neg, out)
   return out


twos_compliment([0x00, 0x00, 0x01])
>>> 1

twos_compliment([0xff,0xff,0xff])
>>> -1

twos_compliment([0b00010010, 0b11010110, 0b10000111])
>>> 1234567

twos_compliment([0b11101101, 0b00101001, 0b01111001])
>>> -1234567

twos_compliment([0b01110100, 0b11001011, 0b10110001])
>>> 7654321

twos_compliment([0b10001011, 0b00110100, 0b01001111])
>>> -7654321

答案 12 :(得分:0)

好吧我的 uLaw压缩算法 PCM wav文件类型有这个问题。而我所发现的是,两个补码有点像某个二进制数as can be seen here的负值。在咨询wikipedia后,我认为它是真的。

这家伙解释说是找到了 least significant bit 并在它之后翻了一遍。我必须说上述所有这些解决方案对我没什么帮助。当我试用0x67ff时,它给了我一些结果,而不是-26623。现在,如果有人知道 least significant bit 正在扫描数据列表,但是由于PCM中的数据不同而我不知道,解决方案可能有效。所以这是我的答案:

max_data = b'\xff\x67' #maximum value i've got from uLaw data chunk to test

def twos_compliment(short_byte): # 2 bytes 
    short_byte = signedShort(short_byte) # converting binary string to integer from struct.unpack i've just shortened it.
    valid_nibble = min([ x*4 for x in range(4) if (short_byte>>(x*4))&0xf ])
    bit_shift = valid_nibble + min( [ x for x in [1,2,4,8] if ( ( short_byte>>valid_nibble )&0xf )&x ] )
    return (~short_byte)^( 2**bit_shift-1 )

data  = 0x67ff
bit4 = '{0:04b}'.format
bit16 = lambda x: ' '.join( map( bit4, reversed([ x&0xf, (x>>4)&0xf, (x>>8)&0xf, (x>>12)&0xf ]) ) )

# print( bit16(0x67ff) , ' : ', bit16( twos_compliment(  b'\xff\x67' ) ) )
# print( bit16(0x67f0) , ' : ', bit16( twos_compliment(  b'\xf0\x67' ) ) )
# print( bit16(0x6700) , ' : ', bit16( twos_compliment(  b'\x00\x67' ) ) )
# print( bit16(0x6000) , ' : ', bit16( twos_compliment(  b'\x00\x60' ) ) )
print( data, twos_compliment(max_data) )

现在,由于代码不可读,我将引导您完成这个想法。

## example data, for testing... in general unknown
data = 0x67ff # 26623 or 0110 0111 1111 1111 

这只是任何十六进制值,我需要测试才能确定,但​​一般情况下它可以是 int 范围内的任何值。所以不要循环遍及 65535 short integer的整群可以让我决定将它分割为 nibbles (4位)。如果您之前没有使用bitwise operators,可以这样做。

nibble_mask = 0xf # 1111
valid_nibble = []

for x in range(4): #0,1,2,3 aka places of bit value
    # for individual bits you could go 1<<x as you will see later

    # x*4 is because we are shifting bit places , so 0xFA>>4 = 0xF
    #     so 0x67ff>>0*4 = 0x67ff
    #     so 0x67ff>>1*4 = 0x67f
    #     so 0x67ff>>2*4 = 0x67
    #     so 0x67ff>>3*4 = 0x6
    # and nibble mask just makes it confided to 1 nibble so 0xFA&0xF=0xA
    if (data>>(x*4))&nibble_mask: valid_nibble.append(x*4) # to avoid multiplying it with 4 later 

因此我们正在搜索 least significant bit ,所以min(valid_nibble )就足够了。在这里,我们得到了第一个活动(带有设置位)半字节的位置。现在我们只需要找到所需的半字节是我们的第一个设置位。

bit_shift = min(valid_nibble)
for x in range(4): 
    # in my example above [1,2,4,8] i did this to spare python calculating 
    ver_data = data>>min(bit_shift ) # shifting from 0xFABA to lets say 0xFA
    ver_data &= nibble_mask # from 0xFA to 0xA 
    if ver_data&(1<<x): 
        bit_shift += (1<<x)
        break

现在我需要澄清一些事情,因为看到~^会让那些不习惯的人感到困惑:

XOR^:2个数字是necesery

这个操作有点不合逻辑,如果两个都是1或0,它会扫描每2位,对于其他一切,它将为0。

 0b10110
^0b11100
--------- 
 0b01010   

另一个例子:

 0b10110
^0b11111
---------
 0b01001

1's complement~ - 不需要任何其他号码

此操作翻转一个数字中的每一位。它与我们所追求的非常相似,但它不会留下最低有效位

0b10110  
~  
0b01001

正如我们在这里看到的那样,1的恭维与数字XOR全套位相同。

现在我们已经相互理解了,我们将通过将所有咬合恢复到补充中的最低位来获得two's complement >。

data = ~data # one's complement of data 

不幸的是,我们的号码中的所有位都被翻了下来,所以我们只需找到一种方法来翻回我们想要的数字。我们可以用bit_shift做到这一点,因为它是我们需要保留的位的位置。因此,当计算数据的数量时,一些位可以保持,我们可以用2**n来做,而对于半字节,我们得到16,因为我们在位值中计算0。

2**4 = 16 # in binary 1 0000 

但我们需要1之后的字节,所以我们可以使用它来减少1的值,我们可以得到。

2**4 -1 = 15 # in binary 0 1111 

让我们看一下具体例子中的逻辑:

 0b110110
 lsb = 2 # binary 10 

~0b110110
----------
 0b001001 # here is that 01 we don't like  

 0b001001
^0b000011 # 2**2 = 4 ; 4-1 = 3 in binary 0b11 
--------- 
 0b001010

我希望这对你或任何有同样问题的新手有所帮助,并研究他们找到解决方案。请记住,我写的代码是frankenstein代码,我为什么要解释它。它可以做得更漂亮,如果有人想让我的代码漂亮,请成为我的客人。

答案 13 :(得分:0)

这里是将十六进制字符串中的每个值转换为二进制补码版本的版本。


In [5159]: twoscomplement('f0079debdd9abe0fdb8adca9dbc89a807b707f')                                                                                                 
Out[5159]: '10097325337652013586346735487680959091'


def twoscomplement(hm): 
   twoscomplement='' 
   for x in range(0,len(hm)): 
       value = int(hm[x],16) 
       if value % 2 == 1: 
         twoscomplement+=hex(value ^ 14)[2:] 
       else: 
         twoscomplement+=hex(((value-1)^15)&0xf)[2:] 
   return twoscomplement            

答案 14 :(得分:0)

您可以使用bit_length()函数将数字转换为它们的二进制补码:

def twos_complement(j):
   return j-(1<<(j.bit_length()))

In [1]: twos_complement(0b111111111111)                                                                                                                                                             
Out[1]: -1

答案 15 :(得分:0)

您可以将整数转换为字节,然后使用struct.unpack进行转换:

from struct import unpack

x = unpack("b", 0b11111111.to_bytes(length=1, byteorder="little"))
print(x)  # (-1,)

答案 16 :(得分:-1)

比这更容易......

对于N位的X: Comp =( - X)&amp; (2 ** N - 1)

def twoComplement(number, nBits):
    return (-number) & (2**nBits - 1)