python中整数除法的基本含义是啥?

Posted

技术标签:

【中文标题】python中整数除法的基本含义是啥?【英文标题】:What's the underlying implimentation of integer division in python?python中整数除法的基本含义是什么? 【发布时间】:2020-02-16 22:31:49 【问题描述】:

我正在运行 python 3.7.3

关于整数除法运算符:“//”(双除法、双正斜杠、双除法运算符?我不确定确切的名称。)

它似乎没有给出一致的结果,而且我发现的许多解释都不能完全解释它的结果。

这里[What is the reason for having '//' in Python?(和其他地方)据说“//”运算符给出没有余数的商。好像a // bfloor(a / b) 相同(如果a / b 为负数,则向上舍入)。

但是,有时它不会给出这个答案。例如,1 // 0.2 的计算结果为 4。但是,1 / 0.2 返回 5,math.floor(1 / 2) 也返回 5。它给出的数字比整数除法应该小一。如果将 10 除以 2,// 运算符会返回 5,但 1 除以 0.2 不能正常工作。

这个问题出现在其他时候我使用// 运算符来划分浮点数。喜欢5 // 0.2100 // 0.2。我不知道这是否是浮点运算的一些怪癖,但如果您输入math.floor(5 / 0.2)(或任何其他给出问题的数字集),这些问题似乎就会消失。除非你除负数,在这种情况下你必须使用math.ceil()而不是math.floor()

我目前的解决方案是这样的:

import math
def integerDivision(a,b):
    tmp = a / b 
    if tmp > 1:
        return(math.floor(tmp))
    else:
        return(math.ceil(tmp))

// 运算符在某些浮点情况下无法给出正确结果的实现是什么?除了上面的代码,有没有更好的方法来解决// 运算符的这个问题?

【问题讨论】:

// 操作时浮点舍入错误不会消失。如果您需要精确的算术,浮点数是错误的工具。 (又名“楼层划分”,顾名思义,总是楼层,从不四舍五入。) 这可能会有所帮助:***.com/questions/5365665/… 请注意,pep 238 确实建议 a//b == floor(a/b) 。所以我觉得这种行为令人惊讶。 【参考方案1】:

这与实施无关。这是关于运算符的语义。无论实现如何,// 运算符都需要为您提供应用于浮点数时看到的结果,并且这些结果确实是正确的(对于浮点数)。如果您不想要这些结果,那么浮点数可能是您正在做的错误的工具。

1 // 0.2 给出浮点数,表示其参数的商的确切值的下限。但是,右侧参数的值与您输入的值不完全一致。右侧参数的值是 64 位 IEEE 二进制浮点可表示的最接近 0.2 的值,略高于 0.2:

>>> import decimal
>>> decimal.Decimal(0.2)
Decimal('0.200000000000000011102230246251565404236316680908203125')

因此,商的确切值略小于 5,因此1 // 0.2 为您提供4.0

1 / 0.2 给你5.0 因为商的确切值不能表示为浮点数。结果需要四舍五入,四舍五入为5.0// 不执行此舍入;它计算精确值的下限,而不是四舍五入的浮点数的下限。 (// 的结果可能需要四舍五入,但这是不同的四舍五入。)

话虽如此,实现需要比floor(x / y) 更复杂,因为那样会给出错误的结果。 CPython 的 // 实现基于 fmod 的浮点数。您可以在 CPython 源代码库中的 Objects/floatobject.c 中查看实现。

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;

其他参数类型将使用其他实现,具体取决于类型。

【讨论】:

现在我明白为什么会这样了。感谢您的 C 代码。但是根据 pep 238,语义是 a//b == floor(a/b)。这让我感到惊讶。

以上是关于python中整数除法的基本含义是啥?的主要内容,如果未能解决你的问题,请参考以下文章

python中整数除法和float到int转换之间存在差异的原因是啥?

在 Python 中,整数除法中向零舍入的好方法是啥?

在函数的签名中,如果星号后面没有标识符名称,那么它在 Python 中的含义是啥? [复制]

python verbose=false是啥含义

这个(冗余?)python表达式的含义是啥?

运算符@在python中的含义是啥[重复]