在Python中将一个16位整数拆分成两个8位整数

12
我需要将给定的16位整数转换为两个8位整数,然后将它们用作输出。接收方会将这两个8位整数重新组合成一个16位输入(很遗憾,我不能控制接收方)。我的解决方案虽然可行,但感觉不够简洁。对于粗略的数字,我是通过位移原始数字来实现的,对于精细的数字,我是取模256来实现的。
那么,我应该使用floor division来得到粗略的数字,还是应该取最低的8位来得到精细的数字(如果是,应该怎么做?)?
或者说我是疯了,以两种不同的方法拆分数字并不是一个问题吗?
def convert(x):
    ''' convert 16 bit int x into two 8 bit ints, coarse and fine.

    '''
    c = x >> 8  # The value of x shifted 8 bits to the right, creating coarse.
    f = x % 256  # The remainder of x / 256, creating fine.
    return c, f

3
只需返回 return divmod(x, 256) - Mark Dickinson
我选择了:c = x >> 8 f = x & 0xff # 这是我之前不知道的。这似乎比 divmod 或 % 和 / 更符合我的操作,也更有可能接近在另一端执行重新组合它们的操作(我希望如此)。 - nrn
7个回答

18

我会这样做

c = (x >> 8) & 0xff
f = x & 0xff

比较安全,如:

>>> (10303 >> 8) & 0xff
40
>>> (1030333333 >> 8) & 0xff
163
>>> (1030333333 >> 8) 
4024739

在 Python 中,你无法 控制 数字是否为 16 位,因此必须将其强制转换为最多 16 位的值。如果你确定有 16 位的值,则不需要这样做,但是这种方式使函数更加通用,并且允许你只关注 16 位的值,而不管容器中包含什么。


阅读“由于在Python中您无法“控制”数字是否为16位”,添加_afaik_并将其解释为表示Python中没有固定的比特宽度容器(类似于uint16),_afaik_。 - ShinTakezou
我应该提到,如果x > 16位,那么情况就变得非常糟糕了,所以如果c > 255,它会在后面抛出一个错误。 - nrn
你可以省略对 c > 255 的检查,或者保留这些检查但不取 AND 运算的结果;如果设计上不能超过 65535,可以省略 & 运算和对 c > 255 的检查。但是,我仍然会像其他人建议的那样使用 & 而不是 %。(在我的回答中进行了编辑,因为复制粘贴后忘记更改。) - ShinTakezou

9
在Python中,位操作并没有特别的优势,所以我会选择以下方式:
c, f= divmod(your_number, 256)

编辑:为了使你的意图更加明显地呈现给那些对二进制有挑战的源代码查看器(如果这样的野兽存在的话),你可以用更加丰富多彩的替代方式来代替简单的256,比如1<<82**80x1000400(Python 3中是0o400)。自Python 2.5以来,由Peephole优化器完成的常量折叠保证任何一种替代方式都与使用256完全相同(我显然是在谈论前两个替代方式,它们是求值为256的表达式;后两个则是常量256)。

$ python
Python 2.6.4 (r264:75706, Dec  7 2009, 18:45:15)
[GCC 4.4.1] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import dis
>>> dis.dis(compile("c, f= divmod(your_number, 1<<8)", "", "exec"))
  1           0 LOAD_NAME                0 (divmod)
              3 LOAD_NAME                1 (your_number)
              6 LOAD_CONST               3 (256)
              9 CALL_FUNCTION            2
             12 UNPACK_SEQUENCE          2
             15 STORE_NAME               2 (c)
             18 STORE_NAME               3 (f)
             21 LOAD_CONST               2 (None)
             24 RETURN_VALUE

1
甚至更好的是,使用 c, f= divmod(your_number, 1<<8) 来清晰地显示您正在第八位拆分 :-) - fortran
1
@fortran:当然可以 :) 我不知怎么想起了一个笑话,其中一个程序员向另一个程序员借1000美元,后者回答说:“为什么我们不把贷款数四舍五入到1024美元呢?” - tzot

2
你说你会将这些数字作为输出,这意味着它们最终会被转换成字符串。考虑到这一点,我建议你看一下 struct 模块,它专门用于将数字打包成二进制数据的字符串。此外,你还可以获得内置的错误检查功能,以防 x 大于 65535(这样,如果程序出现问题,你将收到异常)。例如:
s = struct.pack('>H', x)

是相当于

if x > 65535:
    raise struct.error(...)
c, f = convert(x)
s = chr(c) + chr(f) # big-endian (network) byte ordering

如果您需要另一种字节顺序,您可以编写
s = struct.pack('<H', x)

如果您需要一次转换大量数字,struct.pack 可以批量处理它们:
x = [10333, 10475, 3021, ...] # for example
s = struct.pack('>' + 'H' * len(x), *x)

2

如果操作意图是算术计算,则使用模数和除法保持一致;如果仅进行原始位操作,则使用移位和掩码。


0

这里有一个我喜欢的替代方案:

def convert(x, n_bytes=2, order='big'):
    msb, *lsb = x.to_bytes(n_bytes, byteorder=order)
    return (msb, *lsb)

您可以通过更改 n_bytes 来更改输出字节数。通过设置 order='little',可以切换它们的顺序。

示例输出

In [51]: convert(0x00)
Out[51]: (0, 0)

In [52]: convert(0xff)
Out[52]: (0, 255)

In [53]: convert(0x01ff)
Out[53]: (1, 255)

In [55]: convert(0x7fff)
Out[55]: (127, 255)

如果值太大,会出现错误:

In [54]: convert(0xffffff)
---------------------------------------------------------------------------
OverflowError                             Traceback (most recent call last)
Input In [54], in <cell line: 1>()
----> 1 convert(0xffffff)

Input In [48], in convert(x, n_bytes, order)
      1 def convert(x, n_bytes=2, order='big'):
----> 2     msb, *lsb = (x).to_bytes(n_bytes, byteorder=order)
      3     return (msb, *lsb)

OverflowError: int too big to convert

0

如果您在不同的地方使用数字的两个部分,我建议使用两个独立的函数,但如果您要在同一个地方使用它们,一个函数也可以正常工作。

有几种正确的方法来拆分数字,因此最终归结为个人喜好。 只要您传入的数字最多为16位长,您的代码就可以正常工作。 (这可能不是什么问题,但您应该意识到它)


如果你要分解单个运算符的应用,那么在这种情况下,你应该有一个具体的理由,否则你就不理解分解事物的原因。你发明的函数名可能比任何阅读你代码的人都应该知道的标准运算符更不清晰,当然你还必须去查找函数定义以了解它的真实含义。过度的分解,将复杂性移入调用图中,在我看来是现代替代goto-spaghetti的方法。 - user180247

0

我会使用按位 & 运算符而不是 % 运算符。对于短整数来说,这些天可能没有太大的区别,但从更广泛的视角来看,& 运算符可能更有效率。

关于 % 如何处理负数可能存在一些问题,但我怀疑这在这里并不相关。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接