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

例如:比较浮点数,2012版

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

有标准的库函数吗?


当前回答

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

其他回答

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

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

详见分数模块中的分数。

如果你想在测试/TDD环境中使用它,我认为这是一种标准方法:

from nose.tools import assert_almost_equals

assert_almost_equals(x, y, places=7) # The default is 7

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

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

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

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

我喜欢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))