Python:列表中的Nan是否相等?

时间:2014-12-23 08:43:15

标签: python cpython python-internals

我只是想弄清楚这些结果背后的逻辑:

>>>nan = float('nan')
>>>nan == nan
False 
# I understand that this is because the __eq__ method is defined this way
>>>nan in [nan]
True 
# This is because the __contains__ method for list is defined to compare the identity first then the content?

但在这两种情况下,我认为在幕后的函数PyObject_RichCompareBool被称为正确?为什么会有区别?他们不应该有相同的行为吗?

3 个答案:

答案 0 :(得分:6)

  

但在这两种情况下我都认为幕后功能   PyObject_RichCompareBool被称为对吗?为什么会有区别?   他们不应该有相同的行为吗?

==从不直接在float对象上调用PyObject_RichCompareBool,浮点数有自己的rich_compare方法(调用__eq__),可能会调用PyObject_RichCompareBool也可能不调用 /* Comparison is pretty much a nightmare. When comparing float to float, * we do it as straightforwardly (and long-windedly) as conceivable, so * that, e.g., Python x == y delivers the same result as the platform * C x == y when x and/or y is a NaN. * When mixing float with an integer type, there's no good *uniform* approach. * Converting the double to an integer obviously doesn't work, since we * may lose info from fractional bits. Converting the integer to a double * also has two failure modes: (1) a long int may trigger overflow (too * large to fit in the dynamic range of a C double); (2) even a C long may have * more bits than fit in a C double (e.g., on a a 64-bit box long may have * 63 bits of precision, but a C double probably has only 53), and then * we can falsely claim equality when low-order integer bits are lost by * coercion to double. So this part is painful too. */ static PyObject* float_richcompare(PyObject *v, PyObject *w, int op) { double i, j; int r = 0; assert(PyFloat_Check(v)); i = PyFloat_AS_DOUBLE(v); /* Switch on the type of w. Set i and j to doubles to be compared, * and op to the richcomp to use. */ if (PyFloat_Check(w)) j = PyFloat_AS_DOUBLE(w); else if (!Py_IS_FINITE(i)) { if (PyInt_Check(w) || PyLong_Check(w)) /* If i is an infinity, its magnitude exceeds any * finite integer, so it doesn't matter which int we * compare i with. If i is a NaN, similarly. */ j = 0.0; else goto Unimplemented; } ... 取决于传递给它的参数。

PyObject_RichCompareBool

另一方面,list_contains直接调用项目__eq__,因此在第二种情况下你会得到True。


请注意,这仅适用于CPython,PyPy的list.__contains__方法似乎只是通过调用$~/pypy-2.4.0-linux64/bin# ./pypy Python 2.7.8 (f5dcc2477b97, Sep 18 2014, 11:33:30) [PyPy 2.4.0 with GCC 4.6.3] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>>> nan = float('nan') >>>> nan == nan False >>>> nan is nan True >>>> nan in [nan] False 方法来比较项目:

{{1}}

答案 1 :(得分:1)

您说PyObject_RichCompareBool被调用是对的,请参阅list_contains中的listobject.c函数。

文档说:

  

这相当于Python表达式o1 op o2,其中op是对应于opid的运算符。

然而,这似乎并不完全正确。

在cpython源代码中我们有这一部分:

int
PyObject_RichCompareBool(PyObject *v, PyObject *w, int op)
{
    PyObject *res;
    int ok;

    /* Quick result when objects are the same.
       Guarantees that identity implies equality. */
    if (v == w) {
        if (op == Py_EQ)
            return 1;
        else if (op == Py_NE)
            return 0;
    }

在这种情况下,由于对象是相同的,我们有平等。

答案 2 :(得分:0)

在数学上,将无穷大与无穷大相比较并不会产生sense。这就是为什么没有为nan定义平等的原因。

对于nan in [nan]的情况,引用了不可变变量。但要小心::

>>> nan is nan
True

>>> float('nan') is float('nan')
False

在第一种情况下,引用了不可变变量。在第二个中,创建并比较了两个不同的浮点数。