奇怪的行为:附加到列表后出现浮点错误

Strange Behavior: Floating Point Error after Appending to List

我正在编写一个简单的函数来逐步通过具有浮动步长的范围。为了保持输出整洁,我写了一个函数,correct,它纠正了算术运算后常见的浮点错误。

也就是说:correct(0.3999999999)输出0.4correct(0.1000000001)输出0.1,等等

代码主体如下:

floats = []  
start = 0  
end = 1  
stepsize = 0.1  
while start < end:
    floats.append(start)
    start = correct(start + stepsize)

最后的输出看起来好像还没有更正:
[0, 0.1, 0.2, 0.30000000000000004, 0.4, 0.5, 0.6, 0.7, 0.7999999999999999, 0.8999999999999999, 0.9999999999999999]

为了检查这一点,我插入了一个打印语句以查看附加的内容:

floats = []  
start = 0  
end = 1  
stepsize = 0.1  
while start < end:
    print start
    floats.append(start)
    start = correct(start + stepsize)

这些打印语句的输出是:

0
0.1
0.2
0.3
0.4
0.5
0.6
0.7
0.8
0.9
1.0

所以我知道 correct 功能正常工作。因此,似乎当 start 附加到 floats 时,赋值中发生了某些事情,导致浮点错误再次爆发。

事情变得更糟了。我将变量 z 分配给输出,即 z = [0,0.1,...]。我尝试 print z,它按预期打印出看起来未更正的输出:

[0, 0.1, 0.2, 0.30000000000000004, 0.4, 0.5, 0.6, 0.7, 0.7999999999999999, 0.8999999999999999, 0.9999999999999999]

现在我试试 for a in z: print a。这打印出:

0
0.1
0.2
0.3
0.4
0.5
0.6
0.7
0.8
0.9
1.0

但这与print z中的输出不对应:其中一个有浮点错误,另一个没有。

最后,我尝试 print [correct(x) for x in z]。我又得到了未校正的输出。

我希望我已经弄清楚了我遇到的困惑。为什么打印列表与单独打印每个项目看起来如此不同?为什么列表中的变量不受 correct 函数的影响?我希望这是由于列表中浮点数的某些内部内存表示造成的?

最后,如何确保输出只是[0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9]

我正在使用 Python 2.7.9.

使用 round() 而不是 正确 产生你想要的:

    start = round(start + stepsize, 1)

round() 是内置函数:

round(...)
    round(number[, ndigits]) -> floating point number

    Round a number to a given precision in decimal digits (default 0 digits).
    This always returns a floating point number.  Precision may be negative.

所以我想 正确的 函数可能有问题

我想这里发生的事情是,print 工作正常并显示舍入值,而您的函数没有。

解释起来非常简单:虽然可以将 float 转换为具有适当(四舍五入)值的字符串,但不能将一些浮点值存储在 float 中。所以,你的correct()不能正确的值和return一个float,也会有同样的问题。

这应该是一个常见问题解答,但我找不到与您的问题相关的答案。

Python(几乎所有语言都使用 IEE764 来表示浮点数。这意味着我们干净整洁的小数(0.3、0.2、0.1)没有精确表示为浮点数。只有由 2 的负幂组成的数字可以:0.5、0.25、0.75 具有精确表示。因此,当您尝试对其进行舍入或截断时,处理器会尽力而为,并返回最接近您要求的数字并且它可以表示(单精度数大约 7 位小数,双精度数大约 13 位)

简答:你的 correct 不工作。

长答案:
现代计算机和编程语言中普遍使用的二进制浮点格式无法表示大多数数字,例如 0.1,就像没有终止十进制表示法可以表示 1/3 一样。相反,当您在源代码中写入 0.1 时,Python 会自动将其转换为 3602879701896397/2^55,a.k.a。 0.1000000000000000055511151231257827021181583404541015625,可以用二进制表示。 Python然后就面临打印0.1000000000000000055511151231257827021181583404541015625如何显示的问题

Python 有两种方法来生成事物的字符串表示:strrepr。在 Python 2 中,strrepr 的实现对如何显示浮点数做出了不同的选择。两者都不会产生精确的表示。相反,str 将字符串截断为 12 位数字,这隐藏了一些舍入错误,代价是显示一些非常接近的浮点数相同,而 repr 截断为 17 位数字,足以让不同的浮点数显示总是显示不同。

当您 print 一个浮点数时,Python 使用 str。这意味着您的 print start 语句使它看起来像您的 correct 有效。然而,当你 print 一个列表时,列表的 str 实现在其内容上使用 repr (出于充分的原因我不会在这里讨论),所以 printing该列表显示您的 correct 实际上并没有产生您想要的数字;如果您的 correct 产生了 Python 源代码中 0.3 产生的数字,那么 Python 会将其显示为 0.3.