从二进制文件解包双值

Hil*_*ich 3 python floating-point binary struct python-3.x

我知道在 Python 中读取 C double 值存在问题。在我的程序中,我从二进制文件中读取并将数值转换为各种大小的整数,没有问题。我使用下面的代码来读取双精度值。

peakDescriptor["area"] = struct.unpack("d",file.read(8))
Run Code Online (Sandbox Code Playgroud)

价值观应该是什么和我得到什么之间存在巨大差异。下面的第一个表是我所得到的:
甲烷3.6368230562528605e-307
乙烷-8.243249632731949e + 306
丙烷1.839329701286865e-60
2-甲基-2.55127317345224e-306
丁烷3.737451552798833e + 59
...

该表显示,这些值应该是:
甲烷 97.25
乙烷 426.50
丙烷 2755.60
2-甲基丙烷 3390.25
丁烷 10906.60
...

如何正确读取这些数字?

我的代码可以在这里找到

原始文件和结果文件在这里这里

如果您在访问文件时遇到问题,请告诉我!

PS我已经尝试根据结构文档更改格式字符串以包含“>”符号 - 这仍然会导致意外值以及一些NaN!

Mar*_*son 6

简短回答:您需要使用正确的字节顺序来解释您的字节。事实证明,在这种情况下,正确的字节顺序既不是 little-endian (order 01234567) 也不是 big-endian ( 76543210),而是 order 32107654,因此在使用struct模块之前,您需要进行一点预处理。请参阅interpret_float下面的功能。

更详细地说:首先查看甲烷值,并(相当安全地)猜测您使用的机器是小端的,您从文件中读取的字节如下所示:

>>> field = struct.pack('<d', 3.6368230562528605e-307)
>>> field
b'\x00\x00\x003@XP\x00'
Run Code Online (Sandbox Code Playgroud)

正如您已经发现的那样,尝试将这些字节直接解释为 IEEE 754 binary64(即双精度)浮点值,假设小端字节序或大端字节序不会产生合理的值:

>>> struct.unpack('<d', field)[0]
3.6368230562528605e-307
>>> struct.unpack('>d', field)[0]
1.08755143765e-312
Run Code Online (Sandbox Code Playgroud)

但是, 的字节field和 的预期值的字节之间存在可疑的相似性97.25。如果将字节展开以查看它们的整数值,则更容易查看:

>>> list(struct.pack('<d', 97.25))
[0, 0, 0, 0, 0, 80, 88, 64]
>>> list(field)
[0, 0, 0, 51, 64, 88, 80, 0]
Run Code Online (Sandbox Code Playgroud)

不是一个完美的匹配,但0, 80, 88, 64字节中的序列97.25看起来很像64, 88, 80, 0第二个序列的完全颠倒。除了 little-endian 和 big-endian,还有一对 IEEE 754 双精度浮点数的字节顺序偶尔会出现(通常在 ARM 硬件上),那就是字交换的小端序或字交换的大端序(两者有时都称为混合端或中端)。在您的情况下,看起来好像您拥有的字节是有序的32107654,其中7表示最高有效字节(包含符号位和偏置指数的最高有效 7 位的字节),并且0最低有效字节(包含小数的 8 个最低有效位)。因此,如果我们交换两个单词,我们应该能够将其解释为常规的 big-endian:

>>> def interpret_float(x):
...     return struct.unpack('>d', x[4:] + x[:4])
... 
>>> interpret_float(field)
(97.25000000000072,)
Run Code Online (Sandbox Code Playgroud)

这看起来更有希望!让我们在接下来的几个值上尝试相同的方法。您没有为这些提供原始字节,因此我再次需要根据您提供的错误值对它们进行逆向工程。

>>> ethane_field = struct.pack('<d', -8.243249632731949e+306)
>>> interpret_float(ethane_field)
(426.4999999999999,)
>>> propane_field = struct.pack('<d', 1.839329701286865e-60)
>>> interpret_float(propane_field)
(2755.600000000001,)
Run Code Online (Sandbox Code Playgroud)

从这些来看,我们对 的字节顺序的猜测似乎32107654是正确的。

如果我最初的猜测是错误的并且您实际上在大端机器上,或者您在小端机器上并且您显示的值是通过执行 astruct.unpack('>d', ...)而不是普通的 old 获得的struct.unpack('d', ...),那么字节顺序是45670123,你会需要更换'>d'格式interpret_float'<d'替代。

您可以通过sys.byteorder在 Python 中查看来了解您的主机正在使用的字节顺序。在我的机器和任何其他基于 x86-64 的机器上,它给出'little'

>>> import sys
>>> sys.byteorder
'little'
Run Code Online (Sandbox Code Playgroud)