从二进制文件中解压缩双值

时间:2018-01-23 10:00:23

标签: python python-3.x struct binary floating-point

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

peakDescriptor["area"] = struct.unpack("d",file.read(8))

价值观与我得到的价值之间存在着巨大的差异。下面的第一个表格是我得到的:
甲烷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
...

如何正确阅读这些数字?

我的代码可以是found here

原始文件和结果文件为herehere

如果您在访问文件时遇到问题,请与我们联系!

P.S。我尝试更改格式字符串以包含“>”根据结构文档签名 - 这仍然会导致意外的值,以及一些NaN!

1 个答案:

答案 0 :(得分:1)

简短回答:您需要使用正确的字节顺序来解释您的字节。事实证明,在这种情况下,正确的字节顺序既不是小端(订单01234567)也不是大端(76543210),而是订单32107654,所以你需要一个在使用struct模块之前进行一些预处理。请参阅下面的函数interpret_float

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

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

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

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

但是,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]

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

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

看起来更有前途!让我们对接下来的两个值进行相同的尝试。你没有给出这些的原始字节,所以我再次需要从你给出的错误值中对它们进行逆向工程。

>>> 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,)

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

如果我的原始猜测是错误的并且您实际上是在大端机器上,或者您使用的是小端机器,并且您显示的值是通过执行struct.unpack('>d', ...)而非平原获得的旧的struct.unpack('d', ...),然后字节顺序为45670123,您需要将'>d'格式替换为interpret_float,而不是'<d'

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

>>> import sys
>>> sys.byteorder
'little'