在python中对二进制分数执行右移和位掩码

时间:2018-05-30 13:38:29

标签: python binary bit-shift bitmask

我正在寻找python中的一种方法,对二进制数进行右移和位掩码,二进制数也有一个小数部分。例如,如果数字中有1个整数和2个分数位,则数字0b101对应于十进制的1.25。首先,我想知道在python中表示这个数字的pythonic方法。

其次,我想对此数字执行1次右移(0b101>> 1),以便得到的数字为0b010,十进制为0.5。在python中是否有一种内在的和pythonic方式来执行此操作。同样,如何从二进制数掩码并获取特定位?

目前,对于班次我将数字乘以2 ** - x,x是右移的数量。我无法想到我可以为位掩码执行类似的操作。

2 个答案:

答案 0 :(得分:1)

如果你真的必须直接获得浮动的内部表示,你可以使用struct,如下所示:

>>> import struct
>>> a = 1.25
>>> b = struct.pack('>d',a) 
>>> b
b'?\xf4\x00\x00\x00\x00\x00\x00'  # the ? means \x3f, leftmost 7 bits of exponent
>>> a.hex()
'0x1.4000000000000p+0'   

您可以从struct.pack()返回的字节串中屏蔽您想要的位。

[edit] 表示\x3f的问号是因为bytestring的默认输出表示是 string ,Python会尽可能显示ascii字符,不是两个十六进制数字。

[edit] 这种表示原则上依赖于平台,但实际上并不是这样,因为几乎每台计算机(现在甚至是IBM大型机)都有一个使用的浮点处理器这种格式。

找出你想要的位可能是一个挑战。

>>> c = struct.pack('>d',a/2)
>>> c
b'?\xe4\x00\x00\x00\x00\x00\x00'
>>> (a/2).hex()
'0x1.4000000000000p-1'

正如你所看到的那样,除非你的问题似乎表明你期待的是一个简单的一位向右移动。在这种情况下,除以2将指数递减1(从0x3ff递减到0x3fe;将1023递减到1022)并保持分数(0x4000)的位模式不变。指数看起来很大,因为它偏向1023。

主要困难是

  1. 符号,指数和分数不与字节边界对齐,但与nybble边界对齐(符号加指数:12位;分数:52位)
  2. 对数字进行归一化,使其没有前导零(十进制中的科学记数法被标准化,因此它没有前导零),并且由于每个人都知道它,因此不存储前导1。
  3. 我可以就这个问题推荐Wikpedia article:它有很多有用的例子。

    但我怀疑你并不想真正想要浮动的内部代表。相反,你想要一个 fixed -point二进制类,没有讨厌的二进制指数,它的工作方式与你在纸上的工作方式大致相同,并且2的幂除法确实反映为一个移位右边那么多位。

    根据您想要投入多少工作,您可以通过将FixedBinary类定义为numbers.Real的子类来实现此目的,其中整数部分由一个{{1}内部表示}和另一个int的小数分量,以及第三个int的符号,因此1.25将表示为int(或其他一些2的幂)。

    这也向您展示了获得分数表示的最简单方法。

    [edit] 我建议单独存储标志。您可以将它存储在整数部分或分数或两者中,但都有缺点。

    1. 如果将它存储在分数的符号中,则为二进制补码 负整数的表示会给你带来困难 你想掩盖你的位。
    2. 如果您不将其存储在分数的符号中,则无法进行 代表-0.5。
    3. 如果您不将其存储在整数部分的符号中,则无法表示-1.0。
    4. 65536的被乘数将为您提供4位小数的精度。如果您想要更多,可以增加它。我还建议您将分数存储在最右边的位中,然后忽略最左边的位。换句话说,要满足于(1, int(0.25 * 65536), +1)中间的二进制点,不要坚持它在左边。这是因为当你进行乘法运算时,你需要在二进制点左边的余量。

      但是,实现自己的数字类是相当多的工作。

答案 1 :(得分:1)

您可以使用fxpmath进行工作。

有关此软件包的信息位于: https://github.com/francof2a/fxpmath

以您的示例为例:

from fxpmath import Fxp

x = Fxp('0b0101', signed=True, n_word=4, n_frac=2)

print(x)

y = x >> 1

print(y)

# example of AND mask
z = x & Fxp('0b0110', signed=True, n_word=4, n_frac=2)
print(z.bin())

输出:

1.25
0.5
0100