Это не о реализации. Это о семантике оператора. Независимо от реализации, оператор //
требуется, чтобы дать вам результаты, которые вы видите при применении к плавающим, и эти результаты действительно верны (для плавающих). Если вам не нужны эти результаты, float, вероятно, не тот инструмент, который вы делаете.
1 // 0.2
дает число с плавающей запятой, представляющее пол точного значения отношения его аргументов. , Однако правый аргумент не совсем соответствует введенному вами значению. Значение правого аргумента является наиболее близким к значению 0,2, представляемому в 64-битной двоичной IEEE с плавающей запятой, что немного выше 0,2:
>>> import decimal
>>> decimal.Decimal(0.2)
Decimal('0.200000000000000011102230246251565404236316680908203125')
Таким образом, точное значение коэффициента немного меньше 5, поэтому 1 // 0.2
дает вам 4.0
.
1 / 0.2
дает вам 5.0
, потому что точное значение отношения isn не может быть представлен как поплавок. Результат должен быть округлен, и он округляется до 5.0
. //
не выполняет это округление; он вычисляет пол точного значения, а не пол округленного числа. (Результат округления //
может потребоваться округлить, но это другое округление.)
Учитывая все вышесказанное, реализация должна быть более сложной, чем floor(x / y)
, поскольку это может привести к неправильному результату. результат. CPython основывает свою реализацию //
для чисел с плавающей точкой на fmod
. Вы можете увидеть реализацию в Objects/floatobject.c
в репозитории CPython.
static PyObject *
float_divmod(PyObject *v, PyObject *w)
{
double vx, wx;
double div, mod, floordiv;
CONVERT_TO_DOUBLE(v, vx);
CONVERT_TO_DOUBLE(w, wx);
if (wx == 0.0) {
PyErr_SetString(PyExc_ZeroDivisionError, "float divmod()");
return NULL;
}
PyFPE_START_PROTECT("divmod", return 0)
mod = fmod(vx, wx);
/* fmod is typically exact, so vx-mod is *mathematically* an
exact multiple of wx. But this is fp arithmetic, and fp
vx - mod is an approximation; the result is that div may
not be an exact integral value after the division, although
it will always be very close to one.
*/
div = (vx - mod) / wx;
if (mod) {
/* ensure the remainder has the same sign as the denominator */
if ((wx < 0) != (mod < 0)) {
mod += wx;
div -= 1.0;
}
}
else {
/* the remainder is zero, and in the presence of signed zeroes
fmod returns different results across platforms; ensure
it has the same sign as the denominator. */
mod = copysign(0.0, wx);
}
/* snap quotient to nearest integral value */
if (div) {
floordiv = floor(div);
if (div - floordiv > 0.5)
floordiv += 1.0;
}
else {
/* div is zero - get the same sign as the true quotient */
floordiv = copysign(0.0, vx / wx); /* zero w/ sign of vx/wx */
}
PyFPE_END_PROTECT(floordiv)
return Py_BuildValue("(dd)", floordiv, mod);
}
static PyObject *
float_floor_div(PyObject *v, PyObject *w)
{
PyObject *t, *r;
t = float_divmod(v, w);
if (t == NULL || t == Py_NotImplemented)
return t;
assert(PyTuple_CheckExact(t));
r = PyTuple_GET_ITEM(t, 0);
Py_INCREF(r);
Py_DECREF(t);
return r;
}
Другие типы аргументов будут использовать другие реализации, в зависимости от типов.