十年网站开发经验 + 多家企业客户 + 靠谱的建站团队
量身定制 + 运营维护+专业推广+无忧售后,网站问题一站解决
先说结论:这个问题是由于cpython的地板除运算符(//)的实现不是 浮点除法+floor 来实现而是用了(被除数 - 余数)/除数 导致的。
站在用户的角度思考问题,与客户深入沟通,找到滨海新区网站设计与滨海新区网站推广的解决方案,凭借多年的经验,让设计与互联网技术结合,创造个性化、用户体验好的作品,建站类型包括:成都网站建设、成都网站制作、企业官网、英文网站、手机端网站、网站推广、国际域名空间、网站空间、企业邮箱。业务覆盖滨海新区地区。
PS:Jython下可以得到20.0,而PEP里规定了a // b应该等于round(a/b),所以似乎这是cpython实现的一个bug?
首先先分析下1 / 0.05究竟应该等于多少。答案就是精确的20.0。
简单解释下:IEEE754浮点数规定,如果一个浮点数的值不能被精确记录,那么它的值会被记成与这个数距离最近的可以被IEEE浮点数表示的数。
首先,0.05在二进制下是无限循环小数,自然不能被精确记录,因此0.05这个浮点数的实际值是不等于0.05的,实际值是约为0.05 + 2.7e-18。
之后做浮点除法,实际上做的是1 / (0.05+2.7...e-18),这个除法的结果大约是20 - 1.1e-15。这个值也不能被精确表示,恰好离这个数最近的可以表示的值就是20.0,因此即使有浮点数误差结果也是精确的20.0。
既然1/0.05就是20.0,那么对他做floor运算自然也是20了。
现在的问题就是为什么1 // 0.05会变成19.0,要解决这个问题只能翻源码看//运算符的实现。
直接把cpython/floatobject.c at 829b49cbd2e4b1d573470da79ca844b730120f3d · python/cpython · GitHub 中实现//运算的一段贴上来:
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);
}
可以发现cpython中x // y的实现实际上是
round((x - fmod(x, y)) / y)
,其中fmod函数是求两个浮点数相除的余数。
这样一来就解释的通了:在十进制下,显然1除以0.05的余数应该是0.0。然而在IEEE浮点数环境中,0.05的实际值是约0.05 + 2.7e-18,略大于0.05,这样一来1除以这个数的余数就成了约0.05 - 5e-17,从1中减掉这么多之后就只剩0.95了,除以0.05再round后变成19.0。
在 Python 交互模式下,你可以使用模运算符(%)来表示整数的余数。例如,要求 20 除以 6 的余数,可以使用如下代码:
模运算1
这里,20 除以 6 的余数是 2。
注意,模运算符(%)只能用于求整数的余数,对于浮点数,它是不适用的。如果要求浮点数的余数,可以使用内置函数 math.fmod()。
例如:
模运算2
这里,函数 math.fmod() 返回了浮点数 20 除以 6 的余数 2.0。
先来看一下 math 模块中包含内容,如下所示:
接下来具体看一下该模块的常用函数和常量。
ceil(x)
返回 x 的上限,即大于或者等于 x 的最小整数。看下示例:
floor(x)
返回 x 的向下取整,小于或等于 x 的最大整数。看下示例:
fabs(x)
返回 x 的绝对值。看下示例:
fmod(x, y)
返回 x/y 的余数,值为浮点数。看下示例:
factorial(x)
返回 x 的阶乘,如果 x 不是整数或为负数时则将引发 ValueError。看下示例:
pow(x, y)
返回 x 的 y 次幂。看下示例:
fsum(iterable)
返回迭代器中所有元素的和。看下示例:
gcd(x, y)
返回整数 x 和 y 的最大公约数。看下示例:
sqrt(x)
返回 x 的平方根。看下示例:
trunc(x)
返回 x 的整数部分。看下示例:
exp(x)
返回 e 的 x 次幂。看下示例:
log(x[, base])
返回 x 的对数,底数默认为 e。看下示例:
常量
tan(x)
返回 x 弧度的正切值。看下示例:
atan(x)
返回 x 的反正切值。看下示例:
sin(x)
返回 x 弧度的正弦值。看下示例:
asin(x)
返回 x 的反正弦值。看下示例:
cos(x)
返回 x 弧度的余弦值。看下示例:
acos(x)
返回 x 的反余弦值。看下示例:
decimal 模块为正确舍入十进制浮点运算提供了支持,相比内置的浮点类型 float,它能更加精确的控制精度,能够为精度要求较高的金融等领域提供支持。
decimal 在一个独立的 context 下工作,可以使用 getcontext() 查看当前上下文,如下所示:
从上面的结果中我们可以看到 prec=28,这就是默认的精度,我们可以使用 getcontext().prec = xxx 来重新设置精度。接下来通过具体示例看一下。
基本运算
执行结果:
上面结果是用了默认精度,我们重新设置下精度再来看一下:
执行结果:
random 模块可以生成随机数,我们来看一下其常用函数。
random()
返回 [0.0, 1.0) 范围内的一个随机浮点数。看下示例:
uniform(a, b)
返回 [a, b) 范围内的一个随机浮点数。看下示例:
randint(a, b)
返回 [a, b] 范围内的一个随机整数。看下示例:
randrange(start, stop[, step])
返回 [start, stop) 范围内步长为 step 的一个随机整数。看下示例:
choice(seq)
从非空序列 seq 返回一个随机元素。 看下示例:
shuffle(x[, random])
将序列 x 随机打乱位置。看下示例:
sample(population, k)
返回从总体序列或集合中选择的唯一元素的 k 长度列表,用于无重复的随机抽样。看下示例:
参考: