首页 文章

Python的两个补充

提问于
浏览
49

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

14 回答

  • 3

    好吧我用 uLaw compression algorithm 与PCM wav文件类型有这个问题 . 而且_____8129的补充有点像某个二进制数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 范围内的任何东西 . 所以不要循环遍及整个 65535short 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个数字是necesery

    这个操作有点不合逻辑,如果它们都是1或0,它扫描的每2位,对于其他1,它将为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代码,我为什么要解释它 . 它可以做得更漂亮,如果有人想让我的代码漂亮,请成为我的客人 .

  • 19

    它比所有这些都容易得多......

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

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

    如果最高位为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)
    
  • 0

    它不是内置的,但是如果你想要不寻常的长度数字,那么你可以使用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
    
  • 59
    >>> bits_in_word=12
    >>> int('111111111111',2)-(1<<bits_in_word)
    -1
    

    这是因为:

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

  • 0

    从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
    
  • 8

    几个实现(只是一个插图,不打算使用):

    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
    
  • 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)
    

    这个怎么运作:

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

    为了得到结果,该值向左移1位 . 这会将值的MSB(符号位)移动到 2**bitWidth 的位置 . 当符号位为'0'时,减数变为0,结果为 value - 0 . 当符号位为'1'时,减数变为 2**bitWidth ,结果为 value - 2**bitWidth

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

    • 0xFF - int((0xFF << 1)&2 ** 8)

    • 0xFF - int((0x1FE)&0x100)

    • 0xFF - int(0x100)

    • 255 - 256

    • -1

    示例2:如果参数是值= 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参数 .

  • -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
    

    因此, bitstring ,如the other question中所见,几乎比 int 慢一个数量级 . 但另一方面,'s hard to beat the simplicity—I'm将 uint 转换为位串然后转换为 int ;你必须努力工作,不要理解这一点,或找到任何可以引入错误的地方 . 而作为Scott Griffiths ' answer implies, there'的课程更灵活,可能对同一个应用程序有用 . 但是第三方面,travc 's answer makes it clear what'实际上正在发生 - 即使是新手也应该能够理解从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代码击败它,但您可能不必这样做 .

  • 0

    如果有人需要反方向:

    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

  • 0

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

    这是一个简单的用户定义函数:

    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
    
  • 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 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
    
  • 1

    这适用于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
    

相关问题