如何将四个有符号浮点数打包成一个整数?



我需要能够将 4 个浮点数打包成一个整数,然后将整数解压缩到我的 4 个浮点数中。

浮点数示例(精度不超过 8 位):

-0.02513393, -0.02394553, 0.04248389, 0.02388026

所以,我认为首先我需要将这些浮点数转换为整数,方法是将每个浮点数乘以 1000000000。

floats = [-0.02513393, -0.02394553, 0.04248389, 0.02388026]
integers = list(map(lambda i: int(i * 1000000000), floats))
# output: [-25133930, -23945530, 42483890, 23880260]

然后使用按位运算将四个数字合二为一,如下所示:

a, b, c, d = integers
packed = (a << 24) | (b << 16) | (c << 8) | d

但是,这似乎不对,因为我尝试打包的值是签名的。


您能否提示我正确的解决方案,将此类有符号浮点数打包为单个整数以及解压缩它们的正确方法?


我想在每个负值的末尾添加1,在每个正值的末尾添加0并将整数恢复为浮点数,我会首先检查是否有1我会否定该值,然后除以 1000000000。但这一点也不优雅。

使用 NumPy,您可以将 dtypefloat16的 4 元素数组视为dtypeint64的整数数组:

In [125]: np.array([-0.02513393, -0.02394553, 0.04248389, 0.02388026], dtype=np.float16).view(np.int64)
Out[125]: array([2746396911566169711])

要解压缩 int,您可以使用view(np.float16)

In [126]: np.array([2746396911566169711]).view(np.float16)
Out[126]: array([-0.02513123, -0.02394104,  0.04248047,  0.02388   ], dtype=float16)

请注意,精度会有一些损失。

使用

Python3.2(或更高版本)并且没有 NumPy,您可以将浮点数打包成字节,然后使用int.from_bytes将字节转换为 int。要解包,请使用int.to_bytesstruct.unpack

import struct
def floats_to_int(floats):
return int.from_bytes(struct.pack('4d', *floats), 'big')
def int_to_floats(packed):
return struct.unpack('4d', packed.to_bytes(4*8, 'big'))
floats = [-0.02513393, -0.02394553, 0.04248389, 0.02388026]
print(floats)
packed = floats_to_int(floats)
print(packed)
result = int_to_floats(packed)
print(result)

指纹

[-0.02513393, -0.02394553, 0.04248389, 0.02388026]
3995686615650679380069295189325600154682811585786433559914521688689786263615
(-0.02513393, -0.02394553, 0.04248389, 0.02388026)

如果根据注释,打包数据的宽度无关紧要,则您的一般方法可以通过一些调整来实现。

首先,每个数字8位是不够的
  • ;每个数字之间会有重叠。已知您的浮点数只有 8 位精度,但这并不意味着它们在二进制表示中只有 8 位有效。找出它们需要多宽的一个好方法是考虑一个你知道它们都小于(在你的例子中,1000000000)的数字,那么这个数字的位长度(30)就足够了。所以我们有:

    包装 = A <<90 | B <<60 | C <<30 | D

  • 正如您所怀疑的,这仍然存在负数问题。从上面,我可以用packed & 2**30-1成功地恢复d,用(packed & 2**30-1 << 30 ) >> 30c,但为a做类似的事情,b让我胡说八道。因此,将其简化为您已经解决的问题。如果你给每个人加一个足够大的数字,使它们都是正数,你可以把它们视为无符号 - 再一次,你知道它们小于 1000000000 ,所以有一个神奇的数字。摆弄的数字现在都小于 2000000000,因此我们需要调整字段宽度。所以我们有:

    上限 = 1000000000 包装 = (A + 天花板) <<31*3 |(b + 天花板) <<31*2 |(C + 天花板) <<31 |d

我们可以恢复a((packed & 2**31-1<< 31*3) >> 31*3) - ceiling.为了可读性,您可能需要考虑将其编写为循环。

如评论中所述,您当前的策略不起作用,因为您将 8 位十进制精度与 8精度混为一谈。

(a << 24) | (b << 16) | (c << 8) | d

如果这些变量包含 8 位数据,即 range(256) 中的整数,则会起作用。您需要大约 32 位才能将浮点数据存储为 8位十进制数字精度。

请注意,标准 Python(又名 CPython)使用 IEEE 754 二进制 64 双精度作为其浮点数。

但是,您可以使用 32 位单精度浮点数来近似浮点数据,并使用标准struct模块将它们打包。下面是一个简短的演示:

from struct import pack, unpack
# Make a list of 4 Python floats.
a = [i**0.5 for i in range(5, 9)]
print(a)
# Convert the Python floats to 32 bit floats and pack them into 16 bytes, big endian
fmt = '>ffff'
b = pack(fmt, *a)
print(b, len(b))
# Unpack the bytes back into floats 
z = unpack(fmt, b)
print(z)
print([u*u for u in z])
# Pack the bytes into an int, using big-endian packing
num = int.from_bytes(b, 'big')
print(num)
# Convert the int back to bytes
newb = num.to_bytes(16, 'big')
print(newb, newb == b)    

输出

[2.23606797749979, 2.449489742783178, 2.6457513110645907, 2.8284271247461903]
b'@x0fx1bxbd@x1cxc4q@)Sxfd@5x04xf3' 16
(2.2360680103302, 2.4494898319244385, 2.6457512378692627, 2.8284270763397217)
[5.00000014682206, 6.000000436701214, 6.999999612686736, 7.999999726171666]
85149038802136470295784196693032240371
b'@x0fx1bxbd@x1cxc4q@)Sxfd@5x04xf3' True

请注意,.from_bytes.to_bytes是 Python 3 的特性;Python 2 中的相同操作稍微冗长一些。

最新更新