为什么Python中//和math.floor运算结果会不同

kuaidi.ping-jia.net  作者:佚名   更新日期:2024-07-19
python中^符号为什么会有这种运算结果?

Python里的“^”是按位异或运算符:当两对应的二进位相异时,结果为1。
比如
2^9 首先要换成二进制 0010 与1001 做按位异或,结果是1011,换回十进制就是11

# -*- coding: utf-8 -*-import mathl = []for i in range(101,201): x = int(math.sqrt(i)) #用i 分别除以int(math.sqrt(i))的值,查看i是否是素数 for y in range(2,x+1): #如果是非素数,退出运行 if i % y == 0 and i != y: break if i % y != 0 and i not in l: l.append(i)print l

先说结论:这个问题是由于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中为什么代码后面有括号和中文?
    答:在“#“后面的单行是注释(单行注释),在''' ''' 或者""" """包括的也是注释(多行注释),在这两种情况下可以出现中文。需要注意的是,即使是注释中的中文,也会受到编译器的限制,在字符集不一致的情况下中文注释会变成乱码。再说三种括号,分别对应python的三种数据类型。小括号表示元组(tutle)...
  • python中为什么要讲堆栈
    答:1、因为堆栈是Python中处理数据不可或缺的一部分。2、栈(stack),有些地方称为堆栈,是一种容器,可存入数据元素、访问元素、删除元素,它的特点在于只能允许在容器的一端(称为栈顶端指标,英语:top)进行加入数据(英语:push)和输出数据(英语:pop)的运算。没有了位置概念,保证任何时候可以...
  • Python中字典为什么比列表快?
    答:1、为什么Python中字典比列表快?因为字典中是键-值对(key-value),且字典无顺序、自动去重、占用内存多,用内存换取速度。最重要的是因为字典是hash类型的。2、那什么是hash呢?哈希算法将任意长度的二进制值映射为较短的固定长度的二进制值,这个小的二进制值称为哈希值。哈希值是一段数据唯一且...
  • python中print()为什么会有错?
    答:符号错误。Python def class if elif for while 等语句末尾没有加上“: ”关键符号,检查对应 def class if elif for while语句结尾是否少了关键符号“ : ”。正确代码:class Num(object):这 def 语句结尾添加缺少的 :def __init__(self, num):self.num = numdef __abs__(self):缩进不...
  • Python中为什么推荐使用isinstance来进行类型判断
    答:在Python 3中,类型已经明显减少了很多 types.BuiltinFunctionType types.BuiltinMethodType types.CodeType types.DynamicClassAttribute types.FrameType types.FunctionType types.GeneratorType types.GetSetDescriptorType types.LambdaType types.MappingProxyType types.MemberDescriptorType types.MethodType types...
  • 为什么我在python中输入的中文总不能显示呢?
    答:python报错invalid character in identifier,意思就是“标识符中的无效字符”,检查下有没有字符是中文的,把中文字符改成英文字符再运行就可以了。Python的作者有意的设计限制性很强的语法,使得不好的编程习惯(例如if语句的下一行不向右缩进)都不能通过编译。其中很重要的一项就是Python的缩进规则。一...
  • 为什么python脚本中使用中文会报错? 解决方法已知.. 只想知道详细的原因...
    答:专门花了一下午,总结了Python 2.x中,常见的编码解码方面的错误。更主要的是,先给你解释清楚了背后的逻辑 然后再给你解释,错误的现象,现象背后的原因,以及如何解决,并且教你如何举一反三等等。此处只把几种最常见的问题的原因给你摘录过来:Python中,想要将某字符串解码为对应的Unicode,但是所...
  • 为什么Python必须在方法定义和调用中显式使用“self”?
    答:为什么Python必须在方法定义和调用中显示使用“self”?这个想法借鉴了 Modula-3 语言。出于多种原因它被证明是非常有用的。首先,更明显的显示出,使用的是方法或实例属性而不是局部变量。阅读 self.x 或 self.meth() 可以清楚地表明,即使您不知道类的定义,也会使用实例变量或方法。在 C++ 中,...
  • 在python中出现这种情况为什么
    答:python为什么会出现这种错误?这是 Python 的浮点数精度问题,因为 Python 在存储浮点数的方法是存储二进制的科学计数法。8 字节 64 位存储空间分配了 52 位来存储浮点数的有效数字,11 位存储指数,1 位存储正负号。简单来说,因为小数点后面理论上可以有无限位数,所以不可能在有限字节中精确存储,...
  • 请问python中这个为什么能够替换“xxx”呢?
    答:strings=['abc','xxx','ksl','xxx']index=0 for string in strings:if 'xxx' in string:strings[index]='[censored]'index=index+1 print(strings)源代码(注意源代码的缩进)这个程序能够替换"xxx"的根本原因是变量index的自增和for循环的取列表strings中的元素string是同步的,再加上if语句的...