如何将四个带符号的浮点数打包成一个整数?
How to pack four signed floats into a single integer?
我需要能够将 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,您可以查看 dtype float16
的 4 元素数组作为 dtype int64
:
的整数数组
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
to convert the bytes to an int. To unpack, use int.to_bytes
and 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 digits 精度,但这并不意味着它们在二进制表示中只有 8 个有效 bits。找出它们需要多宽的一个好方法是考虑一个你知道它们都小于的数字(在你的情况下,1000000000),然后该数字的位长度 (30) 就足够了。所以我们有:
打包 = << 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 decimal 位精度和 8 bit 精确。
(a << 24) | (b << 16) | (c << 8) | d
如果这些变量包含 8 位数据,即范围 (256) 内的整数,则可以使用。您需要大约 32 位来将浮点数据存储为 8 十进制 位精度。
请注意,标准 Python(又名 CPython)使用 IEEE 754 binary64 double precision 作为其浮点数。
但是您可以使用 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'@\x0f\x1b\xbd@\x1c\xc4q@)S\xfd@5\x04\xf3' 16
(2.2360680103302, 2.4494898319244385, 2.6457512378692627, 2.8284270763397217)
[5.00000014682206, 6.000000436701214, 6.999999612686736, 7.999999726171666]
85149038802136470295784196693032240371
b'@\x0f\x1b\xbd@\x1c\xc4q@)S\xfd@5\x04\xf3' True
注意.from_bytes
和.to_bytes
是Python3个特征; Python 2 中的相同操作有点冗长。
我需要能够将 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,您可以查看 dtype float16
的 4 元素数组作为 dtype int64
:
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
to convert the bytes to an int. To unpack, use int.to_bytes
and 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 digits 精度,但这并不意味着它们在二进制表示中只有 8 个有效 bits。找出它们需要多宽的一个好方法是考虑一个你知道它们都小于的数字(在你的情况下,1000000000),然后该数字的位长度 (30) 就足够了。所以我们有:
打包 = << 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 decimal 位精度和 8 bit 精确。
(a << 24) | (b << 16) | (c << 8) | d
如果这些变量包含 8 位数据,即范围 (256) 内的整数,则可以使用。您需要大约 32 位来将浮点数据存储为 8 十进制 位精度。
请注意,标准 Python(又名 CPython)使用 IEEE 754 binary64 double precision 作为其浮点数。
但是您可以使用 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'@\x0f\x1b\xbd@\x1c\xc4q@)S\xfd@5\x04\xf3' 16
(2.2360680103302, 2.4494898319244385, 2.6457512378692627, 2.8284270763397217)
[5.00000014682206, 6.000000436701214, 6.999999612686736, 7.999999726171666]
85149038802136470295784196693032240371
b'@\x0f\x1b\xbd@\x1c\xc4q@)S\xfd@5\x04\xf3' True
注意.from_bytes
和.to_bytes
是Python3个特征; Python 2 中的相同操作有点冗长。