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

例如:比较浮点数,2012版

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

有标准的库函数吗?


当前回答

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

其他回答

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

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

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

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

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

return abs(f1 - f2) <= allowed_error

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

这可能是一个有点丑陋的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}'