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

例如:比较浮点数,2012版

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

有标准的库函数吗?


当前回答

不带atol/rtol与给定小数进行比较:

def almost_equal(a, b, decimal=6):
    return '{0:.{1}f}'.format(a, decimal) == '{0:.{1}f}'.format(b, decimal)

print(almost_equal(0.0, 0.0001, decimal=5)) # False
print(almost_equal(0.0, 0.0001, decimal=4)) # True 

其他回答

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

# 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

如果你想比较浮点数,上面的选项很好,但在我的情况下,我最终使用Enum的,因为我只有几个有效的浮点数,我的用例可以接受。

from enum import Enum
class HolidayMultipliers(Enum):
    EMPLOYED_LESS_THAN_YEAR = 2.0
    EMPLOYED_MORE_THAN_YEAR = 2.5

然后运行:

testable_value = 2.0
HolidayMultipliers(testable_value)

如果float是有效的,就没问题,否则它会抛出一个ValueError。

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

详见分数模块中的分数。

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

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

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

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