python中是否有一个内置函数可以将二进制字符串(例如'111111111111')转换为二进制补码整数-1?
19 回答
如果最高位为 1,则二进制补码减去(1<<bits)
。以 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
从二进制字符串开始特别容易......
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)
从 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
它不是内置的,但如果你想要不寻常的长度数字,那么你可以使用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
>>> bits_in_word=12
>>> int('111111111111',2)-(1<<bits_in_word)
-1
这有效,因为:
二进制数的二进制补码定义为通过从 2 的大幂中减去该数获得的值(具体而言,对于 N 位二进制补码,从 2^N 中减去)。在大多数算术中,数字的二进制补码的行为就像原始数字的负数一样,它可以自然地与正数共存。
这将使用按位逻辑有效地为您提供二进制补码:
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**bitWidth)-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 - 整数(0x100)
- 255 - 256
- -1
例2:如果参数是value=0x1F (31d, b11111) and bitWidth=6
- 0x1F - int((0x1F << 1) & 2**6)
- 0x1F - int((0x3E) & 0x40)
- 0x1F - 整数(0x00)
- 31 - 0
- 31
示例 3:值 = 0x80,位宽 = 7
ValueError: Value: 128 out of range of 7-bit value.
示例 4:值 = 0x80,bitWitdh = 8
- 0x80 - int((0x80 << 1) & 2**8)
- 0x80 - int((0x100) & 0x100)
- 0x80 - 整数(0x100)
- 128 - 256
- -128
现在,使用其他人已经发布的内容,将您的位串传递给 int(bitstring,2) 并传递给 twos_complement 方法的 value 参数。
几个实现(只是一个说明,不打算使用):
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
不,没有将二进制补码字符串转换为小数的内置函数。
执行此操作的简单用户定义函数:
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)
请注意,此函数不将位宽作为参数,而是必须使用一个或多个前导零位指定正输入值。
例子:
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
您可以使用 bit_length() 函数将数字转换为二进制补码:
def twos_complement(j):
return j-(1<<(j.bit_length()))
In [1]: twos_complement(0b111111111111)
Out[1]: -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
由于 erikb85 提高了性能,以下是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
。但另一方面,很难超越简单性——我将 a 转换uint
为位串,然后转换为int
; 您必须努力工作才能不理解这一点,或者找到引入错误的任何地方。正如 Scott Griffiths 的回答所暗示的那样,该类有更多的灵活性,这可能对同一个应用程序有用。但另一方面,travc 的回答清楚地说明了实际发生的情况——即使是新手也应该能够理解从无符号整数到 2s 补码有符号整数的转换意味着什么,只需阅读 2 行代码。
无论如何,与另一个关于直接操作位的问题不同,这个问题完全是关于对固定长度的整数进行算术运算,只是大小奇特的整数。所以我猜你是否需要性能,可能是因为你有一大堆这些东西,所以你可能希望它被矢量化。调整 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 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
you could convert the integer to bytes and then use struct.unpack
to convert:
from struct import unpack
x = unpack("b", 0b11111111.to_bytes(length=1, byteorder="little"))
print(x) # (-1,)
我正在使用 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)))
结果 = -1397948151 或 -0x5352fef7 或 '-0b1010011010100101111111011110111'
这适用于 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
使用~
、^
和掩码(掩码决定总位数)
# Given negative value, obtain its two's complement form in 16 bits
>>> mask = (1 << 16) - 1
>>> a = -6
>>> bin(~(a ^ mask))
'0b1111111111111010'
# Given 16-bit signed binary string, return the integer value
>>> mask = (1 << 16) - 1
>>> b = '1111111111110101'
>>> ~(int(b, 2) ^ mask)
-11
好的,我在使用PCM wav 文件类型的uLaw 压缩算法时遇到了这个问题。我发现,二进制补码有点使某些二进制数产生负值,如此处所示。在咨询了维基百科后,我认为这是真的。
那家伙将其解释为寻找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
,我可以决定将它分成半字节(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 个数字是必需的
这个操作有点不合逻辑,对于它扫描的每 2 位,如果两者都是 1 或 0,它将为 0,对于其他所有 1。
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
因为它是我们需要保留的位的位位置。因此,当计算数据的数量时,我们可以使用 nibble 来做到这一点,2**n
因为我们计算的是 0 位的值,所以我们可以得到 16。
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
我希望这对您或任何遇到同样问题并研究他们的 ** 以找到解决方案的新手有所帮助。请记住我写的这段代码是科学怪人代码,这就是我为什么必须解释它的原因。它可以做得更漂亮,如果有人想让我的代码漂亮,请成为我的客人。
这是一个将十六进制字符串中的每个值转换为二进制补码版本的版本。
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
仍然是一个非常相关的问题,但在我的场景中没有一个答案有效 - 这令人惊讶。
这是一个非常简单的函数,用于从整数值计算 n 位 2 的补码整数值。
此函数特别确保返回值不会被 python 视为负值,因为它破坏了 2 补数的本质。
2 的补数最初是为了在本身不支持它们的架构上同时处理正值和负值而创建的。这是一种转换,就是使用可用的位来表示和计算正数和负数。
因此,可以指定位数,默认为 16,并且可以通过将其设置为 0,将其设置为给定值所需的位数。
def twos_comp(val, bits=16):
"""compute the 2's complement of int value """
if bits == 0: # Use as many bits needed for the value.
bits = val.bit_length()
return ((val & (2 ** bits) - 1) - (2 ** bits)) * -1
测试代码:
value = 6752
print(f'{value:05d} = 0x{value:04x} = 0b{value:016b}')
value = twos_comp(value)
print(f'{value:05d} = 0x{value:04x} = 0b{value:016b}')
value = twos_comp(value)
print(f'{value:05d} = 0x{value:04x} = 0b{value:016b}')
测试代码输出:
06752 = 0x1a60 = 0b0001101001100000
01440 = 0x05a0 = 0b0000010110100000
06752 = 0x1a60 = 0b0001101001100000
这比这一切都容易...
对于 N 位上的 X:Comp = (-X) & (2**N - 1)
def twoComplement(number, nBits):
return (-number) & (2**nBits - 1)