Python的两个补充
python中有一个内置的函数,它将二进制string,例如“111111111111”,转换为二进制补码整数 -1?
如果最高位为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 eg, 8bit: 128-255 val = val - (1 << bits) # compute negative value return val # return positive value as is
从二进制string是特别容易…
binary_string = '1111' # or whatever... no '0b' prefix out = twos_comp(int(binary_string,2), len(binary_string))
对我来说更有用的是从hex值(在这个例子中是32位)…
hex_string = '0xFFFFFFFF' # or whatever... '0x' prefix doesn't matter out = twos_comp(int(hex_string,16), 32)
它不是内置的,但如果你想要不寻常的长度数字,那么你可以使用bitstring模块。
>>> from bitstring import Bits >>> a = Bits(bin='111111111111') >>> a.int -1
相同的对象可以通过几种方式等效地创build,其中包括
>>> b = Bits(int=-1, length=12)
它就像一串任意长度的string,并使用属性来获得不同的解释:
>>> print a.int, a.uint, a.bin, a.hex, a.oct -1 4095 111111111111 fff 7777
>>> bits_in_word=12 >>> int('111111111111',2)-(1<<bits_in_word) -1
这是因为:
一个二进制数的二进制补码被定义为从两个大幂中减去该数所得到的值(具体地说,对于一个N比特的二进制补码,从2 ^ N)。 数字的二进制补码在大多数算术中performance得像原始数字的负数,并且可以以自然的方式与正数共存。
几个实现(只是一个说明,不打算使用):
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
自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的答案是好的,但是如果想用整数而不是string,它不适用于负值。 对于每个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
这会给你两个补码有效地使用按位逻辑:
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位)。另一个解决这个问题的方法是按位与(&)值(2 **位宽)-1-
为了得到结果,数值向左移动1位。 这将值(符号位)的MSB移动到与2**bitWidth
。 当符号位为“0”时,减数变为0,结果为value - 0
。 当符号位为1时,减数为2**bitWidth
,结果为value - 2**bitWidth
示例1:如果参数是value = 0xFF(255d,b11111111)和bitWidth = 8
- 0xFF – int((0xFF << 1)&2 ** 8)
- 0xFF – int((0x1FE)&0x100)
- 0xFF – int(0x100)
- 255 – 256
- -1
示例2:如果参数是value = 0x1F(31d,b11111)和bitWidth = 6
- 0x1F – int((0x1F << 1)&2 ** 6)
- 0x1F – int((0x3E)&0x40)
- 0x1F – int(0x00)
- 31 – 0
- 31
示例3:value = 0x80,bitWidth = 7
ValueError: Value: 128 out of range of 7-bit value.
示例4:value = 0x80,bitWitdh = 8
- 0x80 – int((0x80 << 1)&2 ** 8)
- 0x80 – int((0x100)&0x100)
- 0x80 – int(0x100)
- 128 – 256
- -128
现在,使用其他人已发布的内容,将您的bitstring传递给int(bitstring,2)并传递给twos_complement方法的value参数。
由于erikb85提出了performance,这是travc对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
所以,在另一个问题中 , bitstring
比int
慢几乎一个数量级。 但另一方面,很难打败简单 – 我将一个uint
转换为一个位串然后转换为一个int
; 你必须努力不去理解这个,或者find任何地方引入一个bug。 正如斯科特·格里菲斯(Scott Griffiths)的回答所暗示的那样,这个类对于同一个应用程序来说可能有很大的灵活性。 但是,第三方面,travc的答案清楚地说明了实际发生的事情 – 即使是新手也应该能够理解从无符号整数到带符号整数的二进制补码的转换意味着只需要读取两行代码。
无论如何,与其他直接操纵位的问题不同的是,这个问题全是关于在定长整数上进行算术的,只是奇数大小的算术。 所以我猜如果你需要performance的话,可能是因为你有很多这样的东西,所以你可能希望它是vector化的。 调整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代码来打败它,但是你可能不需要。
如果有人也需要反方向:
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
不,不存在将二进制补码二进制string转换为十进制的内置函数。
一个简单的用户定义函数:
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)
请注意,该函数不会将位宽作为参数,而必须使用一个或多个前导零位来指定正值input值。
例子:
In [2]: two2dec('1111') Out[2]: -1 In [3]: two2dec('111111111111') Out[3]: -1 In [4]: two2dec('0101') Out[4]: 5 In [5]: two2dec('10000000') Out[5]: -128 In [6]: two2dec('11111110') Out[6]: -2 In [7]: two2dec('01111111') Out[7]: 127
我正在使用Python 3.4.0
在Python 3中,我们有一些数据types转换的问题。
所以…在这里,我会告诉一个小费(如我),与hexstring工作很多。
我将采取hex数据,并作出补充:
a = b'acad0109' compl = int(a,16)-pow(2,32) result=hex(compl) print(result) print(int(result,16)) print(bin(int(result,16)))
结果= -1397948151或-0x5352fef7或'-0b1010011010100101111111011110111'
不幸的是,没有内置的函数来将一个无符号整数转换为一个二进制补码,但我们可以定义一个函数来使用按位运算:
def s12(value): return -(value & 0b100000000000) | (value & 0b011111111111)
第一个按位和操作用于签名扩展负数(最高有效位被设置),而第二个用于获取剩余的11位。 这是可行的,因为Python中的整数被视为任意精度二进制补码值。
然后,您可以将其与int
函数结合使用,将一串二进制数字转换为无符号整数forms,然后将其解释为一个12位有符号值。
>>> s12(int('111111111111', 2)) -1 >>> s12(int('011111111111', 2)) 2047 >>> s12(int('100000000000', 2)) -2048
这个函数的一个很好的属性是它是幂等的,因此已经签名的值的值不会改变。
>>> s12(-1) -1
这适用于3个字节。 现场代码在这里
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
比这更容易
对于N位上的X:Comp =(-X)&(2 ** N-1)
def twoComplement(number, nBits): return (-number) & (2**nBits - 1)