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

众所周知,由于四舍五入和精确性的问题,比较花车平等是有点费劲。

例如: https : //randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/

什么是在Python中处理这个build议的方式?

这个地方肯定有一个标准的库函数?

Python 3.5添加了PEP 485中描述的math.isclosecmath.isclose函数 。

如果您使用的是早期版本的Python,则在文档中给出等效函数。

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

rel_tol是相对容忍度,它乘以两个参数的大小中较大的一个; 随着价值变得更大,他们之间允许的差异同样仍然被认为是相等的。

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

是不是像以下那么简单?

 return abs(f1 - f2) <= allowed_error 

我会同意Gareth的答案可能是最适合的轻量级function/解决scheme。

但我认为这将是有帮助的,如果你正在使用NumPy或正在考虑它,这是一个包装的function。

 numpy.isclose(a, b, rtol=1e-05, atol=1e-08, equal_nan=False) 

有一点声明:安装NumPy可以是一个不平凡的经验,取决于你的平台。

我不知道在实现Dawson的AlmostEqual2sComplement函数的Python标准库(或其他地方)中的任何东西。 如果这是你想要的行为,你必须自己实现。 (在这种情况下, if abs(ab) <= eps1*(abs(a)+abs(b)) + eps2或类似的if abs(ab) <= eps1*(abs(a)+abs(b)) + eps2而不是使用Dawson的聪明的按位黑客,则可能更好地使用更常规的formstesting为了得到类似Dawson的行为if abs(ab) <= eps*max(EPS,abs(a),abs(b))对于一些小的固定EPS ,你可能会说if abs(ab) <= eps*max(EPS,abs(a),abs(b)) ;这与Dawson不完全一样,但在精神上是相似的。

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

来自评论:

值得注意的是,如果你在做math繁重的工作,并不是绝对需要从小数点开始的精度,这可能会让事情变得糟糕。 花车是方式,快速处理,但不精确。 小数点非常精确,但速度很慢。

浮点数不能用来比较平等的常识是不准确的。 浮点数与整数没有什么不同:如果你计算“a == b”,如果它们是相同的数字,那么你会变成真,否则就会变成假(理解两个NaN当然不是相同的数字)。

实际的问题是:如果我已经做了一些计算,我不能确定这两个数字我必须比较是完全正确的,那么是什么? 这个问题对于浮点和整数是一样的。 如果您评估整数expression式“7/3 * 3”,它将不会与“7 * 3/3”相比较。

所以假设我们问“如何比较整数是否平等?” 在这种情况下 没有单一的答案。 你应该做什么取决于具体的情况,特别是你有什么样的错误,你想达到什么。

这里有一些可能的select。

如果你想得到一个“真实”的结果,如果math上的确切数字是相等的,那么你可以尝试使用你所执行的计算的属性来certificate你在这两个数字中得到相同的错误。 如果这是可行的,并且你比较两个由expression式产生的数字,如果计算得到的数字相等,那么你将从比较中得到“真”。 另一种方法是,您可以分析计算的属性,并certificate错误不会超过一定的数量,可能是相对于其中一个input或其中一个输出的绝对数量或数量。 在这种情况下,您可以询问这两个计算的数字是否至多相差数量,如果在这个间隔内,则返回“真”。 如果你不能certificate一个错误的界限,你可能会猜测和希望最好的。 猜测的一种方法是评估许多随机样本,看看你在结果中得到了什么样的分布。

当然,既然我们只是在math上精确的结果是相等的情况下设置了“真实的”的要求,那么即使它们不相等,我们也保留了“真实”的可能性。 (实际上,我们可以通过总是返回“真实”来满足要求,这使得计算简单但是通常是不可取的,所以我将在下面讨论改进的情况。)

如果你想得到一个“错误的”结果,如果math上的确切数字是不相等的,你需要certificate你的数字的评估产生不同的数字,如果math上的确切数字是不平等的。 在许多常见情况下,这可能是不可能的。 所以让我们考虑一个替代scheme。

一个有用的要求可能是我们得到一个“假”的结果,如果math上的确切数字相差超过一定的数额。 例如,也许我们要计算一个在电脑游戏中出现的球,而我们想知道它是否击中了一只蝙蝠。 在这种情况下,如果球击中球棒,我们当然希望得到“真实”,如果球远离球棒,我们希望得到“假”,如果球在球内,我们可以接受错误的“真实”答案一个math上精确的模拟错过了蝙蝠,但在击中蝙蝠毫米。 在这种情况下,我们需要certificate(或猜测/估计)我们的球的位置和蝙蝠的位置的计算具有至多一毫米(针对所有感兴趣的位置)的组合误差。 如果球和蝙蝠相距超过一毫米,这将使我们总是返回“假”,如果它们接触返回“真”,并且如果它们足够接近以便被接受则返回“真”。

那么,在比较浮点数时,如何决定返回什么取决于您的具体情况。

至于你如何去certificate计算的误差界限,这可能是一个复杂的问题。 任何使用IEEE 754标准的浮点实现在圆到最近模式下都会返回最接近任何基本操作(特别是乘法,除法,加法,减法,平方根)的精确结果的浮点数。 (如果是平局,那么低位是偶数)(特别要小心平方根和除法;你的语言实现可能使用那些不符合IEEE 754的方法)。由于这个要求,我们知道单个结果的误差至多为最低有效位的1/2。 (如果是更多的话,舍入可能会达到一个不同的数字,在数值的1/2以内)。

从那里开始变得复杂得多。 下一步是执行一个操作,其中一个input已经有一些错误。 对于简单的expression式,可以通过计算来跟踪这些错误,以达到最终错误的界限。 在实践中,这只能在less数情况下完成,比如在高质量的math图书馆工作。 而且,当然,您需要精确控制执行哪些操作。 高级语言通常会给编译器带来很大的松弛,所以你可能不知道按什么顺序执行操作。

关于这个话题还有很多可以写的东西,但是我必须在那里停下来。 总而言之,答案是:这个比较没有库例程,因为没有一个解决scheme可以满足大多数需求,这个方法值得放入库例程。 (如果与相对或绝对错误时间间隔相比较,则可以简单地在没有库例程的情况下进行。)

如果你想在testing/ TDD上下文中使用它,我会说这是一个标准的方法:

 from nose.tools import assert_almost_equals assert_almost_equals(x, y, places=7) #default is 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 

我发现下面的比较有帮助:

 str(f1) == str(f2) 

对于某些可能影响源数字表示的情况,可以使用整数分子和分母将它们表示为分数而不是浮点数。 这样你可以有精确的比较。

有关详细信息,请参阅分数分数模块。

这可能有点丑陋,但是当你不需要超过默认浮点精度(大约11位小数)时,它工作的很好。 在Python 2.7上运行良好。

round_to函数使用内置str类中的格式方法将float浮点到一个string,该string表示具有所需小数位数的float,然后将eval内置函数应用于四舍五入浮点数字串以返回浮点数字types。

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

 def round_to(float_num, decimal_precision): return eval("'{:." + str(int(decimal_precision)) + "f}'.format(" + str(float_num) + ")") def is_close(float_a, float_b, decimal_precision): if round_to(float_a, decimal_precision) == round_to(float_b, decimal_precision): return True return False a = 10.0 / 3 # Result: 3.3333333333333335 b = 10.0001 / 3 # Result: 3.3333666666666666 print is_close(a, b, decimal_precision=4) # Result: False print is_close(a, b, decimal_precision=3) # Result: True