【问题标题】:Unpacking double values from binary file从二进制文件中解压缩双精度值
【发布时间】:2018-01-23 10:00:23
【问题描述】:

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

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

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

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

【问题讨论】:

  • Can't you provide a toy data set and your code,所以人们可以重现和识别您的问题?恕我直言,目前没有足够的信息来解决这个问题。
  • 我刚刚添加了源代码文件和数据文件的链接。谢谢。
  • @HillInHarwich:如果您直接在问题中发布相关字节序列会更好。我可以看到 DropBox 文件,但它们很大,我不知道您要解释其中的哪一部分。
  • “我了解到在 Python 中读取 C 双精度值存在问题。” 使用 C doubles 作为它的浮点数。我从未遇到过在 Python 中使用 C 双精度数据的任何问题。
  • 我读到的问题只是与读取的小数位数比 c/c++ 程序记录的多。一个非常小的问题。

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


【解决方案1】:

简短回答:您需要使用正确的字节顺序来解释您的字节。事实证明,在这种情况下,正确的字节顺序既不是 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'

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

>>> 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]

不是完美匹配,但97.25 字节中的0, 80, 88, 64 序列看起来与第二个中序列64, 88, 80, 0 的完全相反。除了 little-endian 和 big-endian 之外,IEEE 754 双精度浮点数还有另外一对偶尔出现的字节顺序(通常在 ARM 硬件上),那就是 word-swapped little-endian 或 word-swapped big-endian (两者有时被称为混合端或中端)。在您的情况下,看起来您拥有的字节是按顺序排列的32107654,其中7 表示最高有效字节(包含符号位和偏置指数的最高有效 7 位的字节),@987654336 @ 最低有效字节(包含小数的 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('&gt;d', ...) 而不是普通的旧 @ 获得的987654341@,则字节顺序为45670123,您需要将interpret_float 中的'&gt;d' 格式替换为'&lt;d'

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

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

【讨论】:

  • 完美!!!这行得通。非常感谢!只是出于好奇——为什么双打会以这种方式编码?!
  • @HillInHarwich:不知道。 :-) 这些值是否来自某种形式的嵌入式硬件?
  • 不,他们没有——所有的结果和面积都是根据原始数据计算的(可以毫无问题地读取为整数)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多