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

例如:比较浮点数,2012版

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

有标准的库函数吗?


当前回答

I'm not aware of anything in the Python standard library (or elsewhere) that implements Dawson's AlmostEqual2sComplement function. If that's the sort of behaviour you want, you'll have to implement it yourself. (In which case, rather than using Dawson's clever bitwise hacks you'd probably do better to use more conventional tests of the form if abs(a-b) <= eps1*(abs(a)+abs(b)) + eps2 or similar. To get Dawson-like behaviour you might say something like if abs(a-b) <= eps*max(EPS,abs(a),abs(b)) for some small fixed EPS; this isn't exactly the same as Dawson, but it's similar in spirit.

其他回答

我同意Gareth的答案可能是最合适的轻量级函数/解决方案。

但我认为,如果您正在使用NumPy或正在考虑使用NumPy,那么有一个打包的函数用于此,这将是有帮助的。

numpy.isclose(a, b, rtol=1e-05, atol=1e-08, equal_nan=False)

不过有一点免责声明:根据您的平台,安装NumPy可能是一种非常重要的体验。

如果你想在测试或TDD上下文中使用pytest包,下面是如何做到的:

import pytest


PRECISION = 1e-3

def assert_almost_equal():
    obtained_value = 99.99
    expected_value = 100.00
    assert obtained_value == pytest.approx(expected_value, PRECISION)

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

# 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__。

这对于你想要确保两个数字是相同的“达到精度”的情况很有用,并且不需要指定公差:

求这两个数的最小精度 将两者舍入到最小精度并进行比较

def isclose(a, b):
    astr = str(a)
    aprec = len(astr.split('.')[1]) if '.' in astr else 0
    bstr = str(b)
    bprec = len(bstr.split('.')[1]) if '.' in bstr else 0
    prec = min(aprec, bprec)
    return round(a, prec) == round(b, prec)

如上所述,它只适用于字符串表示中没有'e'的数字(意思是0.999999999999995e -4 < number <= 0.99999999999999995e11)

例子:

>>> isclose(10.0, 10.049)
True
>>> isclose(10.0, 10.05)
False

做一些像下面这样简单的事情就足够了:

return abs(f1 - f2) <= allowed_error