【问题标题】:"in" statement for lists/tuples of floats浮点数列表/元组的“in”语句
【发布时间】:2016-10-03 10:56:17
【问题描述】:

在处理浮点数的列表/元组时是否应该避免使用 innot in?它的实现是类似于下面的代码还是更复杂?

check = False
for item in list_to_search_the_value_in:
    if value_to_search_for == item:
        check = True

【问题讨论】:

  • 您可能想看看herein 操作符应该是首选,因为它可以利用容器提供的任何特殊容器测试(例如,set.__contains__()list.__contains__() 快很多)。问题在于 float 部分,因为比较来自不同来源的浮点数是否相等通常是数字上的禁忌。
  • 是的。对于usual reasons,最好避免比较浮点数是否相等。
  • 一种可能的替代方法是对浮点数列表进行排序,并使用二进制搜索找到最接近的匹配项,减去并检查差异是否小于给定限制。
  • @dhke: 所以如果我理解正确的话,你是说通常应该使用它,因为它的实现取决于容器类型(集合更快)但是当容器包含浮动时它应该避免。对吗?
  • @Ev.Kounis 是的,但取决于您的用例。如果您知道序列中的数字来自同一来源,那么比较浮点数是否相等并没有什么坏处。但是,如果这些数字来自不同的来源,即一个来自表格,另一个来自用户输入的结果,那么数字错误就会回来咬你。

标签: python


【解决方案1】:

innot in 应该是您首选的会员测试方式。两个运营商都可以使用(通过__contains__())容器提供的任何优化的成员资格测试。

您的问题出在float 部分,因为in== 进行相等比较(首先优化以检查身份)。

一般来说,对于浮点比较是否相等不会产生预期的结果。因此,对于浮动列表,您需要类似

def is_in_float(item, sequence, eps=None):
    eps = eps or 2**-52
    return any((abs(item - seq_item) < eps) for seq_item in sequence)

与排序和二分搜索一起使用,在您方便时找到最接近的匹配浮点数。

【讨论】:

    【解决方案2】:

    Here's 文档的一部分说in 检查序列类型的相等性。所以不,这不应该用于浮点序列。

    【讨论】:

      【解决方案3】:

      in 运算符在后台使用常规相等检查,因此在浮点数方面它与__eq__() 具有相同的限制。谨慎使用。

      >>> 0.3 == 0.4 - 0.1
      False
      
      >>> 0.3 in [0.4 - 0.1]
      False
      

      【讨论】:

        【解决方案4】:

        由于in 运算符使用相等检查,它经常会失败,因为floating point math is "broken"(好吧,不是,但你明白了)。

        您可以使用any 轻松实现类似的功能:

        epsilon = 1e-9
        
        check = any(abs(f - value_to_search_for) < epsilon for f in seq)
        # or
        check = False
        if any(abs(f - value_to_search_for) < epsilon for f in seq):
            check = True
        

        【讨论】:

          【解决方案5】:

          Python 的list 类型有它的__contains__ 方法implemented in C

          static int
          list_contains(PyListObject *a, PyObject *el)
          {
              Py_ssize_t i;
              int cmp;
          
              for (i = 0, cmp = 0 ; cmp == 0 && i < Py_SIZE(a); ++i)
                  cmp = PyObject_RichCompareBool(el, PyList_GET_ITEM(a, i),
                                                     Py_EQ);
              return cmp;
          }
          

          对 Python 的直译可能是:

          def list_contains(a, el):
              cmp = False
              for i in range(len(a)):
                  if cmp: break 
                  cmp = a[i] == el
              return cmp
          

          你的例子是一个更惯用的翻译。

          在任何情况下,正如其他答案所指出的那样,它使用相等来针对您正在检查成员资格的元素测试列表项。使用 float 值可能很危险,因为我们期望相等的数字可能不是由于浮点舍入造成的。

          自己实施检查的更安全的float-安全方式可能是:

          any(abs(x - el) < epsilon for x in a)
          

          epsilon 是一个很小的值。它需要多小取决于您处理的数字的大小,以及您关心的精确度。如果您可以估计可能将el 与列表中的等效值区分开来的数字错误量,您可以将epsilon 设置为大一个数量级,并确信您不会给出假阴性(可能只有在不可能正确的情况下给出误报)。

          【讨论】:

            猜你喜欢
            • 2019-08-14
            • 1970-01-01
            • 2014-09-17
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2022-07-07
            • 2014-02-19
            • 1970-01-01
            相关资源
            最近更新 更多