众所周知,由于舍入和精度问题,比较浮点数是否相等有点棘手。

例如:比较浮点数,2012版

在Python中处理这个问题的推荐方法是什么?

有标准的库函数吗?


当前回答

使用==是一个简单的好方法,如果你不关心公差精确。

# Python 3.8.5
>>> 1.0000000000001 == 1
False
>>> 1.00000000000001 == 1
True

但是要注意0:

>>> 0 == 0.00000000000000000000000000000000000000000001
False

0始终是0。


使用数学。如果你想控制公差,是接近的。

默认值a == b等价于数学。Isclose (a, b, rel_tol=1e-16, abs_tol=0)。


如果你仍然想使用==带有自定义容差:

>>> class MyFloat(float):
        def __eq__(self, another):
        return math.isclose(self, another, rel_tol=0, abs_tol=0.001)

>>> a == MyFloat(0)
>>> a
0.0
>>> a == 0.001
True

到目前为止,我没有找到任何地方配置它全局浮动。此外,mock也不能用于float.__eq__。

其他回答

至于绝对误差,你可以检查一下

if abs(a - b) <= error:
    print("Almost equal")

一些关于Python中浮动行为怪异的信息: Python 3教程03 - if-else,逻辑运算符和初学者常犯的错误

你也可以用数学。相对误差接近。

对于一些可以影响源数表示的情况,可以使用整数分子和整数分母将它们表示为分数而不是浮点数。这样你就可以进行准确的比较。

详见分数模块中的分数。

我发现下面的比较很有帮助:

str(f1) == str(f2)

我喜欢Sesquipedal的建议,但有修改(一个特殊的用例时,两个值都是0返回False)。在我的例子中,我使用的是Python 2.7,只使用了一个简单的函数:

if f1 ==0 and f2 == 0:
    return True
else:
    return abs(f1-f2) < tol*max(abs(f1),abs(f2))

math.isclose()已为此添加到Python 3.5(源代码)。这里是它到Python 2的一个端口。它与Mark Ransom的单行程序的不同之处在于它可以正确地处理“inf”和“-inf”。

def isclose(a, b, rel_tol=1e-09, abs_tol=0.0):
    '''
    Python 2 implementation of Python 3.5 math.isclose()
    https://github.com/python/cpython/blob/v3.5.10/Modules/mathmodule.c#L1993
    '''
    # sanity check on the inputs
    if rel_tol < 0 or abs_tol < 0:
        raise ValueError("tolerances must be non-negative")

    # short circuit exact equality -- needed to catch two infinities of
    # the same sign. And perhaps speeds things up a bit sometimes.
    if a == b:
        return True

    # This catches the case of two infinities of opposite sign, or
    # one infinity and one finite number. Two infinities of opposite
    # sign would otherwise have an infinite relative tolerance.
    # Two infinities of the same sign are caught by the equality check
    # above.
    if math.isinf(a) or math.isinf(b):
        return False

    # now do the regular computation
    # this is essentially the "weak" test from the Boost library
    diff = math.fabs(b - a)
    result = (((diff <= math.fabs(rel_tol * b)) or
               (diff <= math.fabs(rel_tol * a))) or
              (diff <= abs_tol))
    return result