按位运算,移动进位

时间:2017-05-05 16:13:08

标签: python bit-manipulation

目标是能够做到:

struct.pack('!H', x)

但如果x大于65535,那么由于显而易见的原因,这会失败 我不是一个有位操作的向导,但我知道<<操作会丢失任何移位的位。但我不知道如何从二进制/字节字符串中提取一个或多个携带位,并将进位值添加到尾随携带字节的两个字节的集合中。

我需要每次遍历字节字符串2个字节,并将它们与前两个字节相加。有时这会生成一个大于65535的值 - 这是我需要从结果中提取进位的地方 - 并对进位执行+运算(见下图)

这就是我想要完成的事情: (在这种情况下,携带位只有一个,因此2个尾随字节将获得+1。) enter image description here

这是我到目前为止所得到的:

from struct import unpack, pack
from binascii import *

even_bytes_string = b'\x45\x00\x00\x3c\x1c\x46\x40\x00\x40\x06\xac\x10\x0a\x63\x‌​ac\x10\x0a\x0c'
result = None
for i in range(0, len(even_bytes_string)-1,2):
    if not result:
        result = unpack('!H', even_bytes_string[i:i+2])[0]
        continue

    result += unpack('!H', even_bytes_string[i:i+2])[0]
    if result > 65535:
        # Got a carry bit.
        pass

    print(result)
    print(pack('!H', result))

我完全不知道如何执行这个非常简单的任务,而不将add操作的结果转换为字符串格式(11001...)的实际二进制表示。然后执行字符串操作s = s[-16:]+s[:-16](过度简化),最后将其转换回一组2个字节。它不实用,快速或非常“正确”。

我希望你们中的一个人知道你在Python中进行位操作的方法可以告诉我这样做的正确方法是什么。必须有一些。

对于我想要完成的事情(将结果保持在2个字节,删除任何携带位并将其作为“单独”值添加到结果中)的稍微混乱的图片。):enter image description here

2 个答案:

答案 0 :(得分:2)

这个过程只是(x & 0xFFFF) + (x >> 16)。不需要涉及<<操作。这是您引用的worked example的实现:

def addwrap16( a, b ):
    c = a + b
    w = ( c & 0xFFFF ) + ( c >> 16 )
    print(' {:04x} ->  {:016b}'.format(a, a))
    print(' {:04x} ->  {:016b}'.format(b, b))
    print('{:05x} -> {:017b}'.format(c, c))
    print(' {:04x} ->  {:016b}'.format(w, w))
    print('')
    return w

import struct, functools
even_bytes_string = b'\x45\x00\x00\x3c\x1c\x46\x40\x00\x40\x06\xac\x10\x0a\x63\xac\x10\x0a\x0c'
vals = struct.unpack( '!' + 'H' * ( len( even_bytes_string ) // 2 ), even_bytes_string )
result = functools.reduce(addwrap16, vals)

吐出以下内容:

 4500 ->  0100010100000000
 003c ->  0000000000111100
0453c -> 00100010100111100
 453c ->  0100010100111100

 453c ->  0100010100111100
 1c46 ->  0001110001000110
06182 -> 00110000110000010
 6182 ->  0110000110000010

 6182 ->  0110000110000010
 4000 ->  0100000000000000
0a182 -> 01010000110000010
 a182 ->  1010000110000010

 a182 ->  1010000110000010
 4006 ->  0100000000000110
0e188 -> 01110000110001000
 e188 ->  1110000110001000

 e188 ->  1110000110001000
 ac10 ->  1010110000010000
18d98 -> 11000110110011000
 8d99 ->  1000110110011001

 8d99 ->  1000110110011001
 0a63 ->  0000101001100011
097fc -> 01001011111111100
 97fc ->  1001011111111100

 97fc ->  1001011111111100
 ac10 ->  1010110000010000
1440c -> 10100010000001100
 440d ->  0100010000001101

 440d ->  0100010000001101
 0a0c ->  0000101000001100
04e19 -> 00100111000011001
 4e19 ->  0100111000011001

答案 1 :(得分:0)

好的,所以这不是最优雅的解决方案,并且不确定它实际上是否准确,但它至少不会中断struct.pack()

基于@Tryph的一个简单问题,这就是我提出的解决方法:

from struct import unpack, pack
from binascii import *

even_bytes_string = b'\x45\x00\x00\x3c\x1c\x46\x40\x00\x40\x06\xac\x10\x0a\x63\x‌​ac\x10\x0a\x0c'
result = None
for i in range(0, len(even_bytes_string)-1,2):
    if not result:
        result = unpack('!H', even_bytes_string[i:i+2])[0]
        continue

    result += unpack('!H', even_bytes_string[i:i+2])[0]
    if result > 65535:
        tmp = pack('!I', result)
        carry = unpack('!H', tmp[:2])[0]
        result = unpack('!H', tmp[2:])[0]+carry

    print(result)
    print(pack('!H', result))

简单地将较大的数字视为Int而不是Short,这使我能够将两个前置字节作为进位,并在尾随两个字节的顶部添加。它不优雅,但可能有用吗?