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

时间:2010-09-30 19:46:32

标签: python floating-point

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

t = 0.  
while t<4.9:  
    t = t + 0.1  
    if t == 1.:
        ... do something ...

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

''' Case a'''
t = 0.  
while t<4.9:  
    t = t + 0.1  
print(t)
print(t == 5.)

当我跑步时,我得到:

>>> ================================ RESTART ================================
>>>  
5.0  
False  

这是一个惊喜,因为我希望比较测试为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)

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

2 个答案:

答案 0 :(得分:7)

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

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

>>> print(repr(t))
4.999999999999998
>>> print(t == 5.)
False

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

>>> print '{0:.60f}'.format(t)
4.999999999999998223643160599749535322189331054687500000000000
>>> print '{0:.60f}'.format(0.1)
0.100000000000000005551115123125782702118158340454101562500000

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

当你使用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))

结果:

5.0
True

答案 1 :(得分:4)

从不尝试测试浮点数是否相等。

浮动通常是not exactly你输入的内容。

In [43]: .1
Out[43]: 0.10000000000000001

因此,仅测试具有不等式的浮点数会更安全。 如果您需要测试两个浮点数是否几乎相等,请使用近似的实用程序函数:

def near(a,b,rtol=1e-5,atol=1e-8):
    try:
        return abs(a-b)<(atol+rtol*abs(b))
    except TypeError:
        return False

rtol参数允许您指定相对容差。 (abs(a-b)/abs(b)) < rtol

atol参数允许您指定绝对容差。 abs(a-b) < atol

例如,你可以写

t = 0.  
while t<4.9:  
    t = t + 0.1  
    if near(t,1.):
        print('Hiya')