我不知道这是否是一个明显的错误,但在运行 Python 脚本来改变模拟参数时,我意识到 delta = 0.29 和 delta = 0.58 的结果缺失了。经过调查,我注意到以下 Python 代码:
for i_delta in range(0, 101, 1): delta = float(i_delta) / 100 (...) filename = 'foo' + str(int(delta * 100)) + '.dat'
为 delta = 0.28 和 0.29 生成了相同的文件,.57 和 .58 也是如此,原因是 python 将 float(29)/100 返回为 0.289999999999999998。但这不是系统性错误,不是每个整数都会发生的系统性错误。所以我创建了以下 Python 脚本:
import sys n = int(sys.argv[1]) for i in range(0, n + 1): a = int(100 * (float(i) / 100)) if i != a: print i, a
我无法看出出现舍入误差的数字中存在任何规律。为什么这些特定数字会出现这种情况?
这个问题的出现是因为计算机中浮点运算的工作原理。浮点数以二进制表示,而许多小数无法用二进制准确表示。因此,会出现小的舍入误差。
当您执行计算时float(29) / 100,结果无法精确表示为二进制浮点数。相反,它会被表示为最接近的二进制分数。这就是为什么float(29) / 100结果是0.28999999999999998而不是0.29。
float(29) / 100
0.28999999999999998
0.29
这个问题之所以只发生在某些数字上而不发生在其他数字上,是因为 Python 使用的 IEEE 754 标准中浮点数的内部表示。有些十进制分数在二进制中有精确的表示,而其他则没有。例如:
0.5
0.1
0.25
0.01
0.0001100110011001100110011001100110011001100110011001101...
代码中的关键问题是从浮点数到整数的转换以及随后的比较。float(29) / 100乘以 100 后转换为整数时,得到的是int(0.28999999999999998 * 100),28而不是29。
int(0.28999999999999998 * 100)
28
29
该decimal模块提供了一种执行精确十进制运算的方法:
decimal
from decimal import Decimal for i in range(0, 101, 1): delta = Decimal(i) / Decimal(100) filename = 'foo' + str(int(delta * 100)) + '.dat' print(filename) # Verify the output
通过将该值转换为整数之前对其进行四舍五入,您可以缓解此问题:
for i in range(0, 101, 1): delta = float(i) / 100 filename = 'foo' + str(int(round(delta * 100))) + '.dat' print(filename) # Verify the output
您可以在创建文件名时直接格式化浮点值,以确保一致的结果:
for i in range(0, 101, 1): delta = float(i) / 100 filename = 'foo' + f"{delta:.2f}".replace('.', '') + '.dat' print(filename) # Verify the output
此方法使用字符串格式来确保0.29保持29和0.58保持58。
0.58
58
浮点运算问题很常见,源自小数的二进制表示。通过decimal适当使用模数或舍入值,您可以在计算和比较中避免这些陷阱。