首页 > 解决方案 > What happens behind the scene of floor division in Python?

问题描述

The Documentation about the floor division in Python says,

The / (division) and // (floor division) operators yield the quotient of their arguments. The numeric arguments are first converted to a common type. Division of integers yields a float, while floor division of integers results in an integer; the result is that of mathematical division with the ‘floor’ function applied to the result.

I'm focusing on the last line, that says,

the result is that of mathematical division with the ‘floor’ function applied to the result

I get a float in:

>>> 10//2.0
5.0

But an integer in:

>>> import math
>>> math.floor(10/2.0)
5

So it's clearly not the math.floor function that Python floor division is using. I want to know what exact procedure is Python following to compute the floor division of two arguments.

Because when taking the floor division of complex numbers, Python says,

>>> (2+3j)//(4+5j)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can't take floor of complex number.

That means Python takes the division and then applies some sort of floor function. What really is it?

标签: pythonfloor-division

解决方案


比较(都来自该页面):

“地板”功能

功能math.fmod()

如果他们的意思是math.floor,他们会按照他们的风格来设置它的样式fmod,即,作为代码并带有math.前缀、括号和链接。他们的意思只是数学底函数。不是 Python 函数。

深入研究 CPython 是如何实现的,我们先反汇编一下:

>>> import dis
>>> dis.dis('x // y')
  1           0 LOAD_NAME                0 (x)
              2 LOAD_NAME                1 (y)
              4 BINARY_FLOOR_DIVIDE
              6 RETURN_VALUE

抬头看BINARY_FLOOR_DIVIDE

        case TARGET(BINARY_FLOOR_DIVIDE): {
            PyObject *divisor = POP();
            PyObject *dividend = TOP();
            PyObject *quotient = PyNumber_FloorDivide(dividend, divisor);
            Py_DECREF(dividend);
            Py_DECREF(divisor);
            SET_TOP(quotient);
            if (quotient == NULL)
                goto error;
            DISPATCH();
        }

抬头看PyNumber_FloorDivide

PyObject *
PyNumber_FloorDivide(PyObject *v, PyObject *w)
{
    return binary_op(v, w, NB_SLOT(nb_floor_divide), "//");
}

抬头看nb_floor_divide

+-----------------+------------+-----------------+
| Slot            | Type       | special methods |
+-----------------+------------+-----------------+
| ...             | ...        | ...             |
| nb_floor_divide | binaryfunc | __floordiv__    |
| ...             | ...        | ...             |
+-----------------+------------+-----------------+

现在intfloor两者都有自己的:

>>> int.__floordiv__
<slot wrapper '__floordiv__' of 'int' objects>
>>> float.__floordiv__
<slot wrapper '__floordiv__' of 'float' objects>

查找功能_float

static PyNumberMethods float_as_number = {
    ...
    float_floor_div,    /* nb_floor_divide */
    ...
};

抬头看float_floor_div

static PyObject *
float_floor_div(PyObject *v, PyObject *w)
{
    double vx, wx;
    double mod, floordiv;
    CONVERT_TO_DOUBLE(v, vx);
    CONVERT_TO_DOUBLE(w, wx);
    if (wx == 0.0) {
        PyErr_SetString(PyExc_ZeroDivisionError, "float floor division by zero");
        return NULL;
    }
    _float_div_mod(vx, wx, &floordiv, &mod);
    return PyFloat_FromDouble(floordiv);
}

抬头看_float_div_mod

static void
_float_div_mod(double vx, double wx, double *floordiv, double *mod)
{
    ...
    /* snap quotient to nearest integral value */
    if (div) {
        *floordiv = floor(div);
        if (div - *floordiv > 0.5) {
            *floordiv += 1.0;
        }
    }
    ...
}

而且我认为floor它来自 C,因此取决于用于编译 CPython 的任何 C 编译器。


推荐阅读