【问题标题】:Python rounding error with float numbers [duplicate]浮点数的Python舍入错误[重复]
【发布时间】:2011-08-25 05:14:06
【问题描述】:

我不知道这是否是一个明显的错误,但在运行 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.28999999999999998。但这不是系统错误,并不是每个整数都会发生这种情况。所以我创建了以下 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

而且我看不到发生此舍入错误的数字中的任何模式。为什么这些特定数字会发生这种情况?

【问题讨论】:

  • 这就是 IEEE 754 浮点数的工作原理。我建议你四舍五入将浮点数转回整数,而不是简单地截断。
  • 这不是错误 - 它在许多不同的语言中都很常见。有一些解决方法,但在这种情况下,最简单的解决方案可能只是在文件名中使用 idelta。请记住,默认情况下 idelta 不会传递到循环外部。
  • #StdSOAnswer_1。这就是浮点的工作原理。
  • @Tadeck 我会说这仍然是一个错误,它只是现代计算机科学的特有现象。

标签: python


【解决方案1】:

任何不能由 2 的精确幂构成的数字都不能精确地表示为浮点数;它需要近似。有时最接近的近似值会小于实际数字。

阅读What Every Computer Scientist Should Know About Floating-Point Arithmetic

【讨论】:

  • 老实说,在发布同一链接之前,我没有看到您指向同一文档的链接。只是表明它是一个很好的参考。
  • @jimbob,我在原始帖子后一分钟添加了链接。这是经典之作,但我没有立即派上用场。
  • 对于 Pythonistas,Python Tutorial 中还有一个更短(且更易于阅读)的章节来处理此问题。
【解决方案2】:

由于floating point numbers 的性质,它非常知名。

如果你想做十进制运算而不是浮点运算,有libraries 可以做到这一点。

例如,

>>> from decimal import Decimal
>>> Decimal(29)/Decimal(100)
Decimal('0.29')
>>> Decimal('0.29')*100
Decimal('29')
>>> int(Decimal('29'))
29

一般而言,十进制可能会过度,并且在极少数情况下仍然会出现舍入错误,当数字没有有限的十进制表示时(例如,分母不是 1 或不能被 2 或 5 整除的任何分数 - 的因数十进制基数 (10))。例如:

>>> s = Decimal(7)
>>> Decimal(1)/s/s/s/s/s/s/s*s*s*s*s*s*s*s
Decimal('0.9999999999999999999999999996')
>>> int(Decimal('0.9999999999999999999999999996'))
0

所以最好总是在将浮点数转换为整数之前进行四舍五入,除非你想要一个下限函数。

>>> int(1.9999)
1
>>> int(round(1.999))
2

另一种选择是使用来自fractions 库的分数类,它不近似。 (它只是根据需要不断加/减和乘整数分子和分母)。

【讨论】:

  • hmm,实际上更好的例子是 Decimal(1)/Decimal(3) * Decimal(3),它不会产生更精确的 1.0。 “当基数不是 10 时”应该是当分数不能以 10 为基数精确表示时。这个数字当然是以 10 为底的。
  • @DerekLitz - 同意,我的回答很草率。您的示例更简洁(尽管两者都同样有效)。当数字没有以 10 为底的有限十进制表示时应该写,当分母不能被 2 或 5 整除时,任何分数都会发生这种情况。(当然“分数不能以 10 为底精确表示。数字当然,以 10 为底。”也不完全正确。数字没有底。三分之一 = 1 /(1+1+1) 完全与底无关。写成分数可以是以 10--1/3 为基数。)
  • @dr_jimbob 我喜欢上面的改进,但是,我不喜欢“数字没有基数”的说法。也许区别在于语义,但单词的含义很重要。一个数字应该代表一个值(或数量,如果您愿意)。为了创建一个编号系统,需要选择一个基数,需要选择符号,而且我们可以比简单的计数更有效地交流,但我相信这就是你的意思:)
  • @DerekLitz - 是的,数字代表值,但只有数字的表示才有基数。一,二,二十八,三分之二,π是数字。十进制表示分别为:1、2、28、1.5、3.14159...(十进制表示以 10 为底),是的,数字名称通常与以 10 为底有关。在二进制(以 2 为底)中,它们为 1, 10, 11100, 1.1, 11.0010 0100 0011 1111...,以及十六进制:1, 2, 1c, 1.8, 3.243f... 数字具有特定的数学含义,指的是抽象对象(例如,数字二是第二个零的继任者:二=成功(成功零)),不考虑基数。
  • @dr_jimbob 我喜欢这些对话 :)。在“数字”的定义中更多的是模棱两可,这可能意味着代表数学值的抽象或数学值。很高兴知道我何时使用数学类型,我应该假设后者:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-04-02
  • 1970-01-01
  • 2021-04-06
  • 1970-01-01
  • 1970-01-01
  • 2014-07-07
相关资源
最近更新 更多