浮点比较(1.0 == 1.0)总是为假
我在使用Python 2.7.3和Kivy 1.8.0时,想让一个网格(Grid)小部件渐变显示,所以用了下面这个函数:
def __init__(self, **kwargs):
# ...Init parent class here...
self.grid.opacity = 0.0
Clock.schedule_interval(self.show, 1 / 10)
def show(self, value):
if self.grid.opacity == 1.0:
return False
else:
self.grid.opacity += 0.1
show()
这个函数一直在执行,self.grid.opacity == 1.0
总是返回False,所以调度器(scheduler)就一直没有把这个函数移除。
我本以为,文档上说opacity
是一个NumericProperty
,默认值是1.0
,但是我在调用show()
之前已经改变了它的值。
这是我尝试过的:
if self.grid.opacity == NumericProperty(1.0):
if float(self.grid.opacity) == 1.0:
但是这个方法不管用。而且,我确认self.grid.opacity
确实是1.0
,在我进行比较之前,type()
也返回了float
。
这个方法可以用:
if str(self.grid.opacity) == "1.0":
但显然我不喜欢这个解决方案。
有没有什么好的主意?
5 个回答
@Basile Starynkevitch 解释了为什么会出现这种情况,这和浮点数的特性有关。进行这种比较的一般形式是:
abs(numberA - numberB) <= SOMEEPSILON
这里的 SOMEEPSILON
是你认为可以接受的误差范围。
如果你处理的是较小的数字,并且不担心四舍五入的误差,可以使用 sys.float_info.epsilon
。
所以,正如我评论的,结合这两者你可以得到:
abs(self.grid.opacity - 1.0) <= sys.float_info.epsilon
文档中对 epsilon 值的定义是:
1 和大于 1 的最小可表示浮点数之间的差值
换句话说,就是 1 和它前面那个数之间的最小值。
举个例子,如果 Python 只能表示到小数点后两位,epsilon 就是 1.00 和 0.99 之间的差值(实际上这个值要小得多)。
这是你的问题:
>>> q=0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1
>>> str(q)
'1.0'
>>> q
0.9999999999999999
>>> q==1
False
简单来说,永远不要用 == 来比较浮点数。
这可能是浮点数比较的问题。我不知道具体的应用场景,但浮点数从来都不是精确的,所以用它们来判断是否相等可能会出问题。你可以试试下面这种方法:
if abs(float(self.grid.opacity) - 1.0) < .001:
pass
这是一个有趣的浮点数表现的例子,至少在我的环境下是这样的:
>>> .1 + .1 + .1 == .3
False
>>> .1 + .1 == .2
True
这可能不是Python特有的问题。可以看看每个程序员都应该知道的浮点数运算。
0.1这个数字在计算机里并不能被完全准确地表示成一个IEEE754双精度浮点数。所以我猜从0.1
解析出来的浮点数(其实它并不是完全等于十分之一)在转换成字符串“0.1”时可能会出现问题。
正如其他人所说,这个问题是因为浮点数的存储方式造成的。虽然你可以尝试一些变通的方法,但其实有更好的解决办法:Animation
。
在 __init__
里面:
self.grid.opacity = 0
anim = Animation(opacity=1)
anim.start(self.grid)