问题:比较浮点数和Python中几乎相等的最佳方法是什么?
众所周知,由于舍入和精度问题,比较浮点数是否相等。
例如:https: //randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/
在Python中处理此问题的推荐方法是什么?
当然在某个地方有一个标准的库函数吗?
回答 0
Python 3.5添加了PEP 485中描述的math.isclose
和cmath.isclose
功能。
如果您使用的是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
是在所有情况下均按原样应用的绝对公差。如果差异小于这些公差中的任何一个,则认为值相等。
回答 1
如下简单的内容还不够好吗?
return abs(f1 - f2) <= allowed_error
回答 2
我同意Gareth的答案可能最适合作为轻量级功能/解决方案。
但是我认为最好注意一下,如果您正在使用NumPy或正在考虑使用它,则可以使用打包功能。
numpy.isclose(a, b, rtol=1e-05, atol=1e-08, equal_nan=False)
不过有一点免责声明:根据您的平台,安装NumPy可能是不平凡的体验。
回答 3
使用decimal
提供Decimal
类的Python 模块。
从评论:
值得注意的是,如果您正在做大量的数学工作,并且您绝对不需要十进制的精度,那么这确实会使事情陷入困境。浮动是一种方式,处理起来更快,但不精确。小数非常精确,但速度很慢。
回答 4
我不知道Python标准库(或其他地方)中实现Dawson AlmostEqual2sComplement
函数的任何内容。如果这是您想要的行为,则必须自己实施。(在这种情况下,而不是使用Dawson的聪明按位黑客你可能做的更好使用形式的更常规测试if abs(a-b) <= eps1*(abs(a)+abs(b)) + eps2
或类似的道森得到类似的行为,你可能会说,if abs(a-b) <= eps*max(EPS,abs(a),abs(b))
对于一些小的固定EPS
,这是不完全与道森相同,但在精神上相似。
回答 5
无法将浮点数进行相等性比较的常识是不正确的。浮点数与整数没有什么不同:如果计算“ a == b”,则它们是相同的数字将为true,否则为false(要理解两个NaN当然不是相同的数字)。
实际的问题是这样的:如果我进行了一些计算并且不确定我要比较的两个数字是否完全正确,那又是什么?对于浮点和整数,此问题相同。如果计算整数表达式“ 7/3 * 3”,则它将不等于“ 7 * 3/3”。
因此,假设我们问“如何比较整数是否相等?” 在这种情况下。没有一个答案。您应该做什么取决于具体情况,尤其是您遇到的错误类型以及要实现的错误类型。
这是一些可能的选择。
如果要在数学上精确的数字相等的情况下获得“真实”的结果,则可以尝试使用所执行的计算的属性来证明在两个数字中得到相同的错误。如果这是可行的,并且您比较了两个表达式所产生的两个数字,这些表达式在经过精确计算后将得出相等的数字,那么您将从比较中获得“ true”。另一种方法是,您可能会分析计算的属性,并证明误差不会超过特定数量,可能绝对值或相对于输入之一或输出之一的数量。在这种情况下,您可以询问两个计算得出的数字是否相差最大,如果在间隔内,则返回“ true”。如果您无法证明错误界限,您可能会猜测并希望达到最佳。猜测的一种方法是评估许多随机样本,并查看结果中得到的分布类型。
当然,由于我们仅设置了在数学上精确的结果相等的情况下您必须获得“真实”的要求,因此我们就保留了即使它们不相等也获得“真实”的可能性。(实际上,我们可以通过始终返回“ true”来满足要求。这使计算变得简单,但是通常不希望这样做,因此,我将在下面讨论改善这种情况。)
如果要在数学上精确的数字不相等的情况下获得“假”结果,则需要证明在数学上精确的数字不相等的情况下,对数字的评估会得出不同的数字。在许多常见情况下,出于实际目的这可能是不可能的。因此,让我们考虑一个替代方案。
一个有用的要求是,如果数学上精确的数字相差超过一定数量,我们将得到“假”结果。例如,也许我们要计算在计算机游戏中扔出的球在哪里移动,并且我们想知道它是否击中了蝙蝠。在这种情况下,如果球碰到球棒,我们当然想得到“ true”,如果球远离球棒,我们就希望得到“ false”,如果球进入球棒,我们可以接受不正确的“ true”答案。数学上精确的模拟未击中蝙蝠,但击中蝙蝠仅不到一毫米。在这种情况下,我们需要证明(或猜测/估计)我们对球的位置和球拍的位置的计算的组合误差最大为1毫米(对于所有感兴趣的位置)。这将使我们始终返回“
因此,在比较浮点数时如何决定返回什么很大程度上取决于您的具体情况。
关于如何证明计算的误差范围,这可能是一个复杂的主题。使用舍入取整模式使用IEEE 754标准的任何浮点实现都会返回最接近于任何基本运算(尤其是乘法,除法,加法,减法,平方根)的精确结果的浮点数。(在平局的情况下,是舍入的,所以低位是偶数。)(请特别注意平方根和除法;您的语言实现可能会使用不符合IEEE 754的方法。)由于这一要求,我们知道单个结果中的错误最多为最低有效位的值的1/2。(如果更多,则将四舍五入为数值的1/2之内的另一个数字。)
从那里继续进行变得更加复杂。下一步是执行其中一个输入已经有错误的操作。对于简单表达式,可以通过计算跟踪这些错误,以达到最终错误的界限。实际上,这仅在少数情况下才能完成,例如使用高质量的数学库。而且,当然,您需要精确地控制要执行的操作。高级语言通常会给编译器带来很多负担,因此您可能不知道以什么顺序执行操作。
关于这个话题还有很多(现在)可以写,但是我必须到此为止。总而言之,答案是:没有用于此比较的库例程,因为没有适合大多数需求的单一解决方案值得放入库例程中。(如果与一个相对误差间隔或绝对误差间隔进行比较就足够了,则无需库例程即可完成此操作。)
回答 6
如果要在测试/ TDD上下文中使用它,我会说这是一种标准方式:
from nose.tools import assert_almost_equals
assert_almost_equals(x, y, places=7) #default is 7
回答 7
为此,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://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
回答 8
我发现以下比较有帮助:
str(f1) == str(f2)
回答 9
对于某些可能影响源编号表示的情况,可以使用整数分子和分母将它们表示为小数而不是浮点数。这样,您就可以进行精确比较。
有关详细信息,请参见“ 分数的分数”模块。
回答 10
我喜欢@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))
回答 11
对于要确保2个数字相同且“精确度最高”而无需指定公差的情况很有用:
找出2个数字的最小精度
将它们四舍五入到最低精度并进行比较
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.9999999999995e-4 <数字<= 0.9999999999995e11)
例:
>>> isclose(10.0,10.049)
True
>>> isclose(10.0,10.05)
False
回答 12
比较不超过给定十进制数的给定值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
回答 13
这也许有点丑陋,但是当您不需要的默认浮点精度(大约11位小数)时,它就可以很好地工作。
该round_to函数使用格式方法从内置的str类的浮动四舍五入到代表浮球随所需的小数位数,然后用一个字符串应用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所建议的那样,一种构建rount_to函数以避免“ eval”的更简洁方法是使用嵌套格式:
def round_to(float_num, prec):
return '{:.{precision}f}'.format(float_num, precision=prec)
遵循相同的思想,使用很棒的新f字符串(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}'
回答 14
就绝对误差而言,您只需检查一下
if abs(a - b) <= error:
print("Almost equal")
为什么float行为在Python中很奇怪的一些信息 https://youtu.be/v4HhvoNLILk?t=1129
您也可以将math.isclose用于相对错误