在Python中打包和解包二进制浮点数

13

我在使用Python进行二进制文件写入时,对于浮点型数据的打包和解包遇到了一些问题。以下是我的操作:

import struct

f = open('file.bin', 'wb')
value = 1.23456
data = struct.pack('f',value)
f.write(data)
f.close()

f = open('file.bin', 'rb')
print struct.unpack('f',f.read(4))
f.close()
我得到的结果如下:

The result I get is the following:

(1.2345600128173828,)

这些额外的数字是怎么回事?这是一个四舍五入误差吗?这是如何工作的?


1
是的,浮点数本质上是不精确的。 - Martijn Pieters
5
完整的原因,请参阅计算机科学家应该了解的浮点运算知识 - Martijn Pieters
2
Python教程总结了你遇到的表示问题。 - Martijn Pieters
1
如果你想避免失去精度,你可以选择将 Decimal 对象进行 pickle 处理。详见:http://docs.python.org/2/library/pickle.html 和 http://docs.python.org/2/library/decimal.html。 - Aya
2个回答

11
在大多数平台上,Python中的浮点数相当于C语言中的“double”,但您将数据写成了“float”,这种精度只有一半。
如果您使用“double”,则会减少精度损失:
>>> data = struct.pack('d',value)
>>> struct.unpack('d',data)
(1.23456,)
>>> data = struct.pack('f',value)
>>> struct.unpack('f',data)
(1.2345600128173828,)
< p > float 结构格式仅提供 单精度(有效数字精度为 24 位)


这很有道理...如果我理解正确,这意味着浮点小数的精度不能超过0.8388607。这使得它比7位小数略少?当然,我明白指数允许更广泛的数字范围。 - Wilsonator
我认为“(1.2345600128173828,)”只是Python的打印函数显示比实际数据类型更精确的内容,对吗? - Wilsonator
“0.8388607”,你从哪里得到这个数字的?print函数并不显示“更多精度”,它只是将实际存储的值转换为十进制。 - phant0m
我做的是 2^23 = 8388608。 - Wilsonator
4
不。基本上,您可以添加或省略<=24个术语:1、1/2、1/4、1/8、1/16等,然后将总和乘以-2^-126到2^127范围内的二次幂。 - phant0m
我需要更仔细地阅读那个维基。现在我明白了为什么尽可能避免使用浮点数,因为这需要大量的操作来计算值本身,更不用说额外的操作了。 - Wilsonator

2
这是一个十进制转二进制的问题。
你知道某些小数在十进制中是循环的吗?例如,1/3 是 0.3333333 -> 无限循环。1/7 是 0.142857142857[142857] -> 无限循环。
所以这里的关键是:有循环的分数具有一个分母因子,它不是 10 的倍数 —— 例如不是 2 和/或 5 的倍数。
- 1/2 可以整除 - 1/3 有循环 - 1/4 可以整除 - 1/5 可以整除 - 1/6 有循环 - 1/7 有循环 - 1/8 可以整除 - 1/9 有循环 - 1/10 可以整除 - 1/11 有循环 - 等等
那么在二进制中如何运作呢?嗯,这有点糟糕,因为唯一可以整除的因子是 2。除了 2 以外的所有质数都会有无限循环的小数——包括十分之一、百分之一等,其分母中都含有因子 5。1.2345 是 12345/10000,其中分母有因子 2 和 5,而那个 5 意味着你在二进制中有一个无限循环的小数。
但你不能永远重复。这意味着你必须将小数四舍五入,以适应编码浮点数的二进制位。
当你转换回十进制时,舍入误差就会显露出来。
对于编码而言,要点是:尽可能晚地计算除法,以防止这些错误在每次计算中积累。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接