【问题标题】:Testing floating point equality测试浮点相等
【发布时间】:2011-05-01 01:02:22
【问题描述】:

在 python 中是否有测试浮点近似相等的函数?类似的,

 def approx_equal(a, b, tol):
     return abs(a - b) < tol

我的用例类似于 Google 的 C++ 测试库 gtest.h 定义 EXPECT_NEAR

这是一个例子:

def bernoulli_fraction_to_angle(fraction):
    return math.asin(sqrt(fraction))
def bernoulli_angle_to_fraction(angle):
    return math.sin(angle) ** 2
def test_bernoulli_conversions():
    assert(approx_equal(bernoulli_angle_to_fraction(pi / 4), 0.5, 1e-4))
    assert(approx_equal(
              bernoulli_fraction_to_angle(bernoulli_angle_to_fraction(0.1)),
                0.1, 1e-4))

【问题讨论】:

  • 我想你刚刚写的!
  • :) 我只是想他们可能是这个的一个模块......
  • 嗯,一个潜在的问题是tol 可能只大约等于您想要的值。
  • 您不应该与单个容差值进行比较,因为非常大的数字和非常小的数字具有不同的容差。 Numpy 的spacing() 函数会告诉你给定浮点数和最近的浮点数之间的距离,这可能是一种很好的测试方法。
  • Numpy 的 spacing 函数的文档位于 github.com/numpy/numpy/blob/master/numpy/core/code_generators/…(它不会出现在正常的 Google 搜索中)。

标签: python comparison floating-point


【解决方案1】:

如果我是你,我会使用你写的东西,或者把它放在一个单独的模块中(也许与你需要的其他实用程序一起,Python 没有实现)或者放在任何代码需要的顶部它。

您还可以使用 lambda 表达式(我最喜欢的语言功能之一,但可能不太清楚):

approx_equal = lambda a, b, t: abs(a - b) < t

【讨论】:

  • 太好了,谢谢。 (为了以防万一,这个问题会保留一天。)
  • @NeilG:好的,让我们看看大师是否可以解决这个问题并找到更优雅的解决方案:D
  • 这里没有理由使用 lambda,较短的代码不会获得积分,否则代码完全相同。你甚至给它起了个名字……
  • @Rafe Kettler - 是的,但把它想象成麻醉剂。当然,你爱他们。但是有一天你会在水泥地板上醒来,汗流浃背,然后想“为什么这段代码只有在我倒着读时才有意义?”
  • 在 Python 语言中,我发现最好使用“def”关键字来定义命名函数。我也经常使用“lambda”,但仅在不需要定义名称的情况下使用,例如 min() 函数上的 key 参数。我经常在命名函数内部定义命名函数作为命名 for 循环体的一种方式!你会在别处读到 lambda 在 Python 中被严格限制为只能是一个表达式,这在 Python 中是双重糟糕的,因为语句和表达式是两个不同的东西。仅在不需要名称的地方使用 lambda。
【解决方案2】:

在 python 中是否有测试浮点近似相等的函数?

不可能有一个函数,因为定义取决于上下文。

def eq( a, b, eps=0.0001 ):
    return abs(a - b) <= eps

并不总是有效。在某些情况下

def eq( a, b, eps=0.0001 ):
     return abs( a - b ) / abs(a) <= eps

可能更合适。

另外,总是很受欢迎。

def eq( a, b, eps=0.0001 ):
    return abs(math.log( a ) - math.log(b)) <=  eps

这可能更合适。

我看不出您如何要求 a(单个)函数结合所有数学替代方案。因为它取决于应用程序。

【讨论】:

  • 既然他必须自己实现,那他就不能为他的应用程序适当地实现它吗?你基本上是在用一个问题来回答他的问题。
  • "python 中是否有测试浮点近似相等的函数?"这个问题——正如所问的——是一个糟糕的问题。答案是无用的“不”。由于这个问题很糟糕,我们必须继续讨论一个相关的问题,比如“为什么不呢?”更有用的相关问题的答案是“不能有函数”。如单数。这不是内置的,因为构建它没有意义。您总是必须自己实现它。 “答案”是提问者已经知道的无用的“不,没有功能”。
  • 不要指定任意的 epsilon 值。这是定义的:from sys.float_info import epsilon
  • @Vince 您使用的 epsilon 值必然取决于您的比较环境 — 特别是,您不能只使用机器 epsilon(sys.float_info.epsilonFLT/DBL_EPSILON in C)无处不在(这是一个常见的错误)。是的,如果有意义的话,您可以将任意值乘以机器 epsilon 来计算合适的值,但不提倡仅使用机器 epsilon。
【解决方案3】:

另一种方法是计算两个数字的relative change(或相对差),“用于比较两个数量,同时考虑被比较事物的‘大小’”。 Wikipedia 文章中提到的两个 formulas 可用于 Python 中的以下比较,它还处理被比较的一个或两个值为零的情况:

def approx_equal(a, b, tol):
    return abs(a-b) <= max(abs(a), abs(b)) * tol

def approx_equal(a, b, tol):
    return abs(a-b) <= (abs(a)+abs(b))/2 * tol

任何一种情况下的计算值都是无单位分数。在第一种情况下,基线值是两个数字的最大绝对值,在第二种情况下,它是它们的平均绝对值。本文更详细地讨论了每种方法以及它们的优缺点。如果在比较之前乘以 100,后者可以变成 percentage differencetol 成为百分比值)。请注意,文章建议如果变化的值“本身就是一个百分比,最好用百分比来谈论它的变化”——即绝对变化。

这两种方法(显然)都需要比简单地取两个数字之差的绝对值多一点的计算,这可能是一个考虑因素。

【讨论】:

  • max(abs(a)-abs(b)) 减少到 max(single_value) 这会引发 TypeError,因为 max() 需要一个可迭代对象。
  • 不客气。互联网无所不知 互联网无所不知。 :-)
  • 注意:如果您执行 approx_equal(0, 0, .00001) 会得到 ZeroDivisionError,即使使用 assertAlmostEqual 比较相等的工作也是如此
  • 而不是除以可以为零的max(abs(a), abs(b)),您应该将此术语移到右侧,即要求&lt; tol * max(abs(a), abs(b))
  • @Andre:不完全是,&lt; 也必须更改为&lt;=。不管这是一个很好的建议 - 答案已更新。谢谢。
【解决方案4】:

比较浮点数是否相等通常是个坏主意。即使您使用了容差功能,这也不是您真正想要做的。

如果您想使用浮点数,一个合理的选择是重构您的算法以使用不等式,a &lt; b,因为这更有可能达到您的预期,并且误报或误报要少得多,最重要的是,这意味着您不必猜测它们必须相等才能相等。

如果您不能这样做,另一种选择是使用精确表示。如果您的算法仅由算术运算(+-*/)组成,那么您可以使用fractions.Fraction 提供的理性表示,或者decimal.Decimal 是您想要的(例如,财务计算)。

如果您的算法不能用任意精度表示轻松表达,另一种选择是使用区间算术显式管理舍入误差,例如使用this module

【讨论】:

  • 在您以两种不同方式计算值的测试中呢?
  • 在这种特定情况下,区间数学是要走的路。每个操作都会跟踪它将导致的舍入误差量。
【解决方案5】:
  • 比较数字,有math.isclose
  • 对于比较数字或数组,有numpy.allclose
  • 对于测试数字或数组,有numpy.testing.assert_allclose

【讨论】:

  • 这实际上只是对unittest的assertAlmostEqual的重定向,所以即使你不使用nose也可以使用它。
  • 如果你想比较数组并逐个得到所有比较的结果:[True, False, ...]numpy.isclose()也可以使用。
【解决方案6】:

根据the tutorial

...虽然无法使数字更接近其预期的精确值,但 round() 函数可用于后舍入,以便具有不精确值的结果可以相互比较...

因此,这就是我在 Python 中定义“isclose”函数的方式:

def isclose(a, b, ndigits):
   return round(a-b, ndigits) == 0

我一般用5作为ndigits;但是,这取决于您期望的精度。

【讨论】:

  • 感谢您的回答您应该阅读导致 isclose 被采纳的鼓励
猜你喜欢
  • 1970-01-01
  • 2011-04-19
  • 1970-01-01
  • 2018-12-10
  • 2017-10-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多