在Python中比较几乎相等的浮点的最佳方法是什么?

在Python中比较几乎相等的浮点的最佳方法是什么?,python,floating-point,Python,Floating Point,众所周知,由于舍入和精度问题,比较浮点数是否相等有点麻烦 例如: 在Python中,建议采用什么方法来处理此问题 这里肯定有一个标准的库函数吗?像下面这样简单的东西还不够好吗 return abs(f1 - f2) <= allowed_error 返回abs(f1-f2)像下面这样简单的东西还不够好吗 return abs(f1 - f2) <= allowed_error returnabs(f1-f2)使用Python的模块,该模块提供了Decimal类 从评论中: 值得

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

例如:

在Python中,建议采用什么方法来处理此问题


这里肯定有一个标准的库函数吗?

像下面这样简单的东西还不够好吗

return abs(f1 - f2) <= allowed_error

返回abs(f1-f2)像下面这样简单的东西还不够好吗

return abs(f1 - f2) <= allowed_error
returnabs(f1-f2)使用Python的模块,该模块提供了
Decimal

从评论中:

值得注意的是,如果你 做数学是一项繁重的工作,而你却没有 绝对需要的精度 十进制,这真的会让事情变得一团糟 放下。花车的速度快得多 处理,但不精确。小数是 非常精确但很慢

使用Python的模块,它提供了
Decimal

从评论中:

值得注意的是,如果你 做数学是一项繁重的工作,而你却没有 绝对需要的精度 十进制,这真的会让事情变得一团糟 放下。花车的速度快得多 处理,但不精确。小数是 非常精确但很慢


我不知道Python标准库(或其他地方)中有任何实现Dawson的
AlmosteQual2SComplete
函数的东西。如果这是你想要的行为,你必须自己实施。(在这种情况下,如果abs(a-b)我不知道Python标准库(或其他地方)中有任何内容,那么最好使用更为传统的测试形式
,而不是使用Dawson聪明的逐位攻击。)这实现了Dawson的
AlmosteQual2sComplete
函数。如果这是您想要的行为,您必须自己实现它。(在这种情况下,与其使用Dawson聪明的按位破解,不如使用更传统的形式
If abs(a-b)测试。)关于不能比较浮点数是否相等的普遍看法是不准确的。浮点数与整数没有区别:如果计算“a==b”,则如果它们是相同的数字,则为真;否则为假(需要理解的是,两个“n”当然不是相同的数字)

实际的问题是:如果我做了一些计算,但不确定我要比较的两个数字是否完全正确,那又怎样?这个问题对于浮点和整数都是一样的。如果你计算整数表达式“7/3*3”,它将不等于“7*3/3”

因此,假设我们在这种情况下问“我如何比较整数的相等性?”没有一个单一的答案;你应该做什么取决于具体情况,特别是你有什么样的错误以及你想要实现什么

这里有一些可能的选择

如果你想得到一个“真实的”结果如果数学上精确的数字相等,那么您可以尝试使用您执行的计算的属性来证明您在这两个数字中得到相同的错误。如果这是可行的,并且您比较从表达式中得出的两个数字,如果精确计算,则会得到“true”通过比较。另一种方法是,您可以分析计算的属性,并证明误差永远不会超过某个量,可能是绝对量,也可能是相对于一个输入或一个输出的量。在这种情况下,您可以询问两个计算数的差值是否最多为该量,然后如果它们在区间内,则返回“true”。如果无法证明错误界限,则可能会猜测并希望获得最佳结果。猜测的一种方法是评估许多随机样本,并查看结果中的分布类型

当然,由于我们只设置了在数学上精确的结果相等时得到“真”的要求,因此我们保留了即使它们不相等也得到“真”的可能性。(事实上,我们可以通过始终返回“真”来满足要求)。这使计算变得简单,但通常是不可取的,因此我将在下面讨论改进情况。)

如果你想得到一个“假”的结果,如果数学上精确的数字是不相等的,你需要证明,如果数学上精确的数字是不相等的,你对数字的评价会产生不同的数字。在许多常见的情况下,这可能是不可行的。所以,让我们考虑另一种选择。 一个有用的要求可能是,如果数学上精确的数字相差超过一定数量,我们会得到一个“假”结果。例如,我们可能要计算一个在电脑游戏中投出的球在哪里移动,我们想知道它是否击中了球棒。在这种情况下,我们当然想得到“真”如果球击中球棒,如果球远离球棒,我们希望得到“假”,如果在数学上精确的模拟中球没有击中球棒,但距离击中球棒不到一毫米,我们可以接受错误的“真”答案。在这种情况下,我们需要证明(或猜测/估计)我们对球的位置和球棒的位置的计算有一个最大一毫米的组合误差(对于所有感兴趣的位置)。这将允许我们在球和球棒之间的距离超过一毫米时始终返回“假”,如果球和球棒接触时返回“真”,如果球和球棒足够接近可以接受时返回“真”

因此,在比较浮点数时,您如何决定返回什么在很大程度上取决于您的具体情况

至于如何证明计算的误差范围,这可能是一个复杂的问题。任何使用IEEE 754标准的浮点实现,在舍入到最近模式下,都会返回最接近任何基本运算(尤其是乘法、除法、加法、子运算)精确结果的浮点数
str(f1) == str(f2)
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)
from nose.tools import assert_almost_equals

assert_almost_equals(x, y, places=7) #default is 7
def isclose(a, b, rel_tol=1e-09, abs_tol=0.0):
    '''
    Python 2 implementation of Python 3.5 math.isclose()
    https://hg.python.org/cpython/file/tip/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
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
def round_to(float_num, prec):
    return '{:.{precision}f}'.format(float_num, precision=prec)
def round_to(float_num, prec):
    return f'{float_num:.{prec}f}'
def is_close(a, b, prec):
    return f'{a:.{prec}f}' == f'{b:.{prec}f}'
if f1 ==0 and f2 == 0:
    return True
else:
    return abs(f1-f2) < tol*max(abs(f1),abs(f2))
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)                               
>>> isclose(10.0,10.049)
True
>>> isclose(10.0,10.05)
False
if abs(a - b) <= error:
    print("Almost equal")
# Python 3.8.5
>>> 1.0000000000001 == 1
False
>>> 1.00000000000001 == 1
True
>>> 0 == 0.00000000000000000000000000000000000000000001
False
>>> 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