在循环内修改变量时测试数字相等性

Ren*_*ené 4 python floating-point

我是python的新手,我写的是:

t = 0.  
while t<4.9:  
    t = t + 0.1  
    if t == 1.:
        ... do something ...
Run Code Online (Sandbox Code Playgroud)

我注意到if语句从未被执行过.所以我修改了代码看起来像这样:

''' Case a'''
t = 0.  
while t<4.9:  
    t = t + 0.1  
print(t)
print(t == 5.)
Run Code Online (Sandbox Code Playgroud)

当我运行这个时,我得到:

>>> ================================ RESTART ================================
>>>  
5.0  
False  
Run Code Online (Sandbox Code Playgroud)

这是一个惊喜,因为我希望比较测试为True.然后,我尝试了以下两种情况:

''' Case b'''
t = 0
while t<5:
    t = t + 1
print(t)
print(t == 5)

''' Case c'''
t = 0.
while t<5:
    t = t + 0.5
print(t)
print(t == 5)
Run Code Online (Sandbox Code Playgroud)

当我运行最后两个案例(b和c)时,最终语句中的比较测试为True.我不明白为什么会这样或为什么行为似乎不一致.我究竟做错了什么?

Mar*_*ers 7

问题是二进制浮点运算不精确,因此在计算中会出现小错误.特别地,数字0.1没有精确的二进制表示.使用浮点数计算时,非常小的错误会导致结果与您的预期略有不同,这会导致相等测试失败.

使用默认字符串表示形式打印float时,可​​能无法看到此小错误.请尝试使用repr,因为这样可以更精确地表示数字(但仍然不是100%准确):

>>> print(repr(t))
4.999999999999998
>>> print(t == 5.)
False
Run Code Online (Sandbox Code Playgroud)

要获得浮点数的准确字符串表示形式,您可以使用以下format方法:

>>> print '{0:.60f}'.format(t)
4.999999999999998223643160599749535322189331054687500000000000
>>> print '{0:.60f}'.format(0.1)
0.100000000000000005551115123125782702118158340454101562500000
Run Code Online (Sandbox Code Playgroud)

浮点运算的一般规则是永远不要进行相等比较.

当你使用0.5时它工作的原因是因为0.5确实具有二进制浮点数的精确表示,因此在这种情况下你没有看到任何问题.同样,它适用于0.25或0.125.

如果需要精确计算,则可以使用十进制类型.

from decimal import Decimal    
step = Decimal('0.1')

t = Decimal(0)
while t < Decimal(5):  
    t += step 

print(t)
print(t == Decimal(5))
Run Code Online (Sandbox Code Playgroud)

结果:

5.0
True