我需要能够将 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_bytes
和struct.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 ) >> 30
c
,但为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 中的相同操作稍微冗长一些。