为什么 9007199254740993 != 9007199254740993.0?

Why does 9007199254740993 != 9007199254740993.0?

这个比较的结果让我很吃惊(CPython 3.4):

>>> 9007199254740993 == 9007199254740993.0
False

我对the docs的理解是左操作数应该转换为float以匹配右操作数的类型:

Python fully supports mixed arithmetic: when a binary arithmetic operator has operands of different numeric types, the operand with the “narrower” type is widened to that of the other, where integer is narrower than floating point, which is narrower than complex. Comparisons between numbers of mixed type use the same rule. The constructors int(), float(), and complex() can be used to produce numbers of a specific type.

这似乎没有发生:

>>> float(9007199254740993) == 9007199254740993.0
True

这是怎么回事?

Python 并没有准确地 将整数转换为浮点数;它将浮点数转换为整数:

>>> 9007199254740993 == int(9007199254740993.0)
False

失败,因为 int(9007199254740993.0) 实际上是 9007199254740992:

>>> 9007199254740992 == 9007199254740993.0
True

float_richcompare() function。具体来说,它前面的评论:

/* Comparison is pretty much a nightmare. 

 [...]

 * 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) an 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 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.

这两个数字会发生什么:

  • Python 尝试 int.__eq__(float) 路线,但是 returns NotImplemented
  • Python 尝试 float.__eq__(int) 路由,float_richcompare() 处理。

在该函数中,v 是您的浮点数,w 是整数。以下是为该路径执行的代码选择:

else if (PyLong_Check(w)) {   /* true because the other number is an Python integer */

    /* ... */

    nbits = _PyLong_NumBits(w);   /* 54 for your integer */

    /* ... */

    if (nbits <= 48) {  /* nope, can't make it a float outright */
        /* ... */
    }

    (void) frexp(i, &exponent);  /* the exponent is 54 for your float */

    if (exponent < 0 || (size_t)exponent < nbits) {
        /* not true */
    }
    if ((size_t)exponent > nbits) {
        /* also not true */
    }
    /* v and w have the same number of bits before the radix
     * point.  Construct two ints that have the same comparison
     * outcome.
     */
    {
        /* code to convert v to an integer vv, copy w to ww */

        r = PyObject_RichCompareBool(vv, ww, op);

        /* ... */

        result = PyBool_FromLong(r);

        /* ... */

        return result;
    }

所以最后由于涉及的数大小,Python将浮点数转换为整数,也就是浮点数的转换结果是9007199254740992。那是因为浮点数实际上不能准确地表达 9007199254740993.0

>>> 9007199254740993.0
9007199254740992.0