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

例如:比较浮点数,2012版

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

有标准的库函数吗?


当前回答

Python 3.5增加了数学运算。Isclose和cmath。isclose函数如PEP 485所述。

如果您使用的是较早版本的Python,相应的函数在文档中给出。

def isclose(a, b, rel_tol=1e-09, abs_tol=0.0):
    return abs(a-b) <= max(rel_tol * max(abs(a), abs(b)), abs_tol)

Rel_tol是一个相对容差,它乘以两个参数的大小中较大的一个;当值变大时,它们之间允许的差异也会变大,但仍然认为它们相等。

Abs_tol是在所有情况下按原样应用的绝对容差。如果差值小于这些公差中的任何一个,则认为值相等。

其他回答

如果你想比较浮点数,上面的选项很好,但在我的情况下,我最终使用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。

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

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

这可能是一个有点丑陋的hack,但当你不需要超过默认的浮点精度(大约11个小数)时,它工作得很好。

round_to函数使用内置str类中的format方法将浮点数四舍五入为表示浮点数的字符串,其中包含所需的小数数,然后将eval内置函数应用于四舍五入的浮点数字符串,以返回浮点数字类型。

is_close函数只是对四舍五入的浮点数应用一个简单的条件。

def round_to(float_num, prec):
    return eval("'{:." + str(int(prec)) + "f}'.format(" + str(float_num) + ")")

def is_close(float_a, float_b, prec):
    if round_to(float_a, prec) == round_to(float_b, prec):
        return True
    return False

>>>a = 10.0
10.0
>>>b = 10.0001
10.0001
>>>print is_close(a, b, prec=3)
True
>>>print is_close(a, b, prec=4)
False

更新:

正如@stepehjfox所建议的,构建一个避免“eval”的rount_to函数的更干净的方法是使用嵌套格式:

def round_to(float_num, prec):
    return '{:.{precision}f}'.format(float_num, precision=prec)

遵循同样的思想,使用新的f-string (Python 3.6+)代码可以更简单:

def round_to(float_num, prec):
    return f'{float_num:.{prec}f}'

所以,我们甚至可以用一个简单干净的'is_close'函数来概括它:

def is_close(a, b, prec):
    return f'{a:.{prec}f}' == f'{b:.{prec}f}'

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

return abs(f1 - f2) <= allowed_error

Python 3.5增加了数学运算。Isclose和cmath。isclose函数如PEP 485所述。

如果您使用的是较早版本的Python,相应的函数在文档中给出。

def isclose(a, b, rel_tol=1e-09, abs_tol=0.0):
    return abs(a-b) <= max(rel_tol * max(abs(a), abs(b)), abs_tol)

Rel_tol是一个相对容差,它乘以两个参数的大小中较大的一个;当值变大时,它们之间允许的差异也会变大,但仍然认为它们相等。

Abs_tol是在所有情况下按原样应用的绝对容差。如果差值小于这些公差中的任何一个,则认为值相等。