为什么 Python 的 math.ceil ()和 math.floor ()操作返回 float 而不是整数?

有人能解释一下这个吗(直接从 医生强调地雷) :

Ceil (x) 返回 x 作为花车的上限,即大于或等于 x 的最小 整数值。

返回 x作为花车的最大值小于或等于 x 的 整数值。

根据定义,.ceil.floor应该计算整数,为什么它们要返回浮点数?


编辑:

关于为什么 应该返回浮点数,这里有一些很好的论据,我刚刚习惯了这个想法,当 @ jcolado指出它们实际上是,在 Python 3中的 返回整数时..。

116320 次浏览

也许是因为其他语言也这样做,所以这是普遍接受的行为。(如其他答案所示,理由充分)

因为 python 的数学库是返回 float 的 C 数学库的一个薄包装器。

浮点数的范围通常超过整数的范围。通过返回一个浮点值,这些函数可以为位于整数可表示范围之外的输入值返回一个有意义的值。

考虑一下: 如果 floor()返回一个整数,那么 floor(1.0e30)应该返回什么?

现在,虽然 Python 的整数现在是任意精度的,但它并不总是这样。标准库函数是等效 C 库函数的瘦包装器。

因为浮点数的范围大于整数的范围——返回一个整数可能会溢出

在 Python 2.4之前,整数不能包含所有被截断的实数。

Http://docs.python.org/whatsnew/2.4.html#pep-237-unifying-long-integers-and-integers

正如其他答案所指出的,在 python 中,它们返回 float 可能是由于防止溢出问题的历史原因。但是,它们在 python3中返回整数。

>>> import math
>>> type(math.floor(3.1))
<class 'int'>
>>> type(math.ceil(3.1))
<class 'int'>

您可以在 PEP 3141中找到更多信息。

你困惑的根源在于你的评论:

Ceil/floor 操作的全部要点就是将浮点数转换为整数!

Ceil 和 floor 操作的要点是将浮点数据舍入到 整数值。不进行类型转换。需要获取 整数值的用户可以在操作之后执行显式转换。

注意,如果所有可用的都是返回整数的 ceil 或 float 操作,那么就不可能实现从舍入到整数的值。您首先需要检查输入是否在可表示的整数范围内,然后调用函数; 您需要在单独的代码路径中处理 NaN 和无穷大。

此外,如果您想要符合 IEEE 754,则必须具有返回浮点数字的 ceil 和 floor 版本。

这是一个非常有趣的问题!由于浮点数需要一些位来存储指数(= bits_for_exponent) ,任何大于 2**(float_size - bits_for_exponent)的浮点数都将是一个整数值!在另一个极端,带有负指数的浮点数将给出 10-1中的一个。这使得关于 整数范围2**(float_size - bits_for_exponent)0的讨论毫无意义,因为只要数字超出了整数类型的范围,这些函数就会返回原始数字。Python 函数是 C函数的包装器,所以这实际上是 C函数的一个缺陷,在 C函数中,它们应该返回一个整数,并强制程序员在调用 ceil/floor 之前执行 range/NaN/Inf检查。

因此,逻辑上的答案是,只有在这些函数有用的时候,它们才会返回一个整数范围内的值,因此,它们返回一个浮点数的事实就是一个 错误,您非常聪明地意识到了这一点!

最近这完全让我措手不及。这是因为我从20世纪70年代就开始用 C 语言编程,现在才开始学习 Python 的细节。就像 math.floor ()的这种奇怪的行为。

Python 的数学库是如何访问 C 标准数学库的。C 标准数学库是浮点数字函数的集合,比如 sin ()、 cos ()、 sqrt ()。数值计算上下文中的 floor ()函数总是返回一个 float。已经50年了。这是数值计算标准的一部分。对于那些熟悉 C 语言数学库的人来说,我们不能理解它仅仅是“数学函数”。我们认为它是浮点算法的集合。它可以更好地命名为 NFPAL-数值浮点算法库之类的东西。:)

了解这段历史的人会立即将 python 数学模块看作是长期建立的 C 浮点库的包装器。因此,我们不假思索地期望 math.floor ()与 C 标准库 floor ()是相同的函数,后者接受一个 float 参数并返回一个 float 值。

Floor ()作为一个数值数学概念的使用可以追溯到1798年,维基百科上有关这个主题的页面: https://en.wikipedia.org/wiki/Floor_and_ceiling_functions#Notation

它从来就不是一个计算机科学的隐式浮点整数存储格式函数,即使在逻辑上它是一个类似的概念。

这个上下文中的 floor ()函数一直是一个浮点数值计算,就像数学库中的所有(大多数)函数一样。浮点数超出了整数的能力范围。它们包括 + inf、-inf 和 Nan (不是一个数字)的特殊值,这些值都在如何通过浮点数值计算传播方面得到了很好的定义。在数值计算中,Floor ()总是正确地保留了 Nan 和 + inf 和-inf 这样的值。如果 Floor 返回一个 int,它就完全打破了数值 Floor ()函数的整个概念。Floor (float (“ nan”))必须返回“ nan”才能成为真正的浮点数字 floor ()函数。

当我最近看到一个 Python 教育视频告诉我们使用:

i = math.floor(12.34/3)

为了得到一个整数,我暗自嘲笑教练是多么愚蠢。但是在写一个刻薄的评论之前,我做了一些测试,令我震惊的是,我发现 Python 中的数值算法库返回了一个 int。更奇怪的是,我认为从除法得到整数的最明显的答案是:

i = 12.34 // 3

为什么不使用内置的整数除法来获得您正在寻找的整数!从我的 C 背景来看,这是显而易见的正确答案。但是在这种情况下,Python 中的整数除法将返回一个 FLOAT!哇!巨蟒是个多么奇怪的倒立世界啊。

Python 中的一个更好的回答是,如果真的需要 int 类型,那么应该直接在 Python 中要求使用 int:

i = int(12.34/3)

然而,请记住,floor ()向负无穷大方向转,int ()向零方向转,因此它们对负数给出不同的答案。因此,如果负值是可能的,则必须使用函数来给出应用程序所需的结果。

然而,Python 是一种不同的野兽,这是有充分理由的。它试图解决一个与 C 不同的问题集。Python 的静态类型对于快速的原型设计和开发来说是非常好的,但是当用一种类型的对象(比如 float)测试的代码在传递 int 参数时出现微妙的失败并且很难找到方法时,它会产生一些非常复杂和难以发现的 bug。正因为如此,Python 做出了许多有趣的选择,在其他历史规范的基础上尽可能减少意外错误。

改变除法总是返回一个浮点数(或某种形式的非整数)是正确的方向。同样的道理,使//成为一个 floor (a/b)函数,而不是一个“ int 除法”也是合乎逻辑的。

将 float 除以零作为一个致命错误而不是返回 float (“ inf”)同样是明智的,因为在大多数 Python 代码中,除以零不是一个数值计算,而是一个编程错误,其中的数学是错误的,或者有一个关闭的错误。对于一般的 Python 代码来说,更重要的是在 bug 发生时捕捉到它,而不是以“ inf”的形式传播一个隐藏的错误,从而导致远离实际 bug 的崩溃。

只要语言的其他部分在需要的时候能够很好地将 int 转换为 float,比如在除法或 math.sqrt ()中,那么让 math.floor ()返回 int 就是合乎逻辑的,因为如果以后需要作为 float,它就会被正确地转换回 float。如果程序员需要一个 int 函数,那么函数就会给他们所需要的。Floor (a/b)和 a/b 应该是一样的,但事实上它们不是,我想这只是历史的问题,还没有调整到一致性。也许因为向下兼容问题很难“修复”。也许没那么重要?

在 Python 中,如果您想编写核心的数值算法,正确的答案是使用 NumPy 和 SciPy,而不是内置的 Python 数学模块。

import numpy as np


nan = np.float64(0.0) / 0.0    # gives a warning and returns float64 nan


nan = np.floor(nan)            # returns float64 nan

Python 是不同的,这是有原因的,而且理解它需要一些时间。我们可以看到,在这种情况下,OP,谁不理解数值 floor ()函数的历史,需要和期望它返回一个 int 从他们的数学整数和实数的思想。现在 Python 正在做我们的数学(与计算机科学)训练所暗示的事情。这使得它更有可能做到初学者期望它做的事情,同时仍然用 NumPy 和 SciPy 覆盖高级数值算法的所有更复杂的需求。Python 的进化给我留下了深刻的印象,即使有时候我完全措手不及。