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

时间:2011-12-20 22:22:09

标签: python math

有人能解释一下(直接来自docs - 强调我的):

  

math.ceil(x)将x 的上限作为float 返回,最小的整数值大于或等于x。

     

math.floor(x)将x 的最低值作为浮动返回,最大整数值小于或等于x。

为什么.ceil.floor在按照定义计算整数时会返回浮点数?


修改

这有一些非常好的论据,为什么他们应该返回浮点数,而我只是习惯了这个想法,当@jcollado指出它们实际上是 在Python 3中返回ints ...

9 个答案:

答案 0 :(得分:94)

浮点数的范围通常超出整数范围。通过返回浮点值,函数可以为位于可表示的整数范围之外的输入值返回合理的值。

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

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

答案 1 :(得分:89)

正如其他答案所指出的那样,在python中,他们返回浮点数可能是因为历史原因可以防止溢出问题。但是,它们在python 3中返回整数。

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

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

答案 2 :(得分:17)

因为python的数学库是C数学库的一个薄包装器,它返回浮点数。

答案 3 :(得分:16)

你的评论中可以看出你的困惑的来源:

  

ceil / floor操作的重点是将浮点数转换为整数!

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

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

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

答案 4 :(得分:5)

在Python 2.4之前,整数无法保存截断实数的全部范围。

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

答案 5 :(得分:4)

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

答案 6 :(得分:4)

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

因此逻辑答案是这些函数唯一有用的时候它们会在整数范围内返回一个值,因此它们返回一个浮点数是错误,你很聪明地意识到这一点!

答案 7 :(得分:1)

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

答案 8 :(得分:0)

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

Python 的数学库是您访问 C 标准数学库的方式。 C 标准数学库是浮点数值函数的集合,如 sin()、cos()、sqrt()。数值计算上下文中的 floor() 函数总是返回一个浮点数。现在 50 年了。它是数值计算标准的一部分。对于我们这些熟悉 C 数学库的人来说,我们不理解它只是“数学函数”。我们将其理解为浮点算法的集合。最好将其命名为 NFPAL - Numerical Floating Point Algorithms Libary。 :)

我们这些了解历史的人会立即将 python 数学模块视为历史悠久的 C 浮点库的包装器。因此,我们毫不犹豫地期望 math.floor() 与 C 标准库 floor() 是相同的函数,它接受一个浮点参数并返回一个浮点值。

使用 floor() 作为数值数学概念可以追溯到 1798 年的维基百科页面:https://en.wikipedia.org/wiki/Floor_and_ceiling_functions#Notation

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

此上下文中的 floor() 函数一直是浮点数值计算,就像数学库中的所有(大多数)函数一样。浮点数超出了整数所能做的范围。它们包括 +inf、-inf 和 Nan(不是数字)的特殊值,它们都很好地定义了它们如何通过浮点数值计算传播。在数值计算中,Floor() 始终正确地保留了像 Nan 和 +inf 和 -inf 这样的值。如果Floor 返回一个int,则它完全打破了数字floor() 函数的意图的整个概念。 math.floor(float("nan")) 必须返回 "nan" 如果它是一个真正的浮点数值 floor() 函数。

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

i = math.floor(12.34/3)

为了得到一个整数,我暗自笑着老师是多么的无知。但是在写下一个尖刻的评论之前,我做了一些测试,令我震惊的是,我发现 Python 中的数值算法库返回了一个 int。更奇怪的是,我认为从除法中获得 int 的明显答案是使用:

i = 12.34 // 3

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

在 Python 中更好的答案是,如果你真的需要一个 int 类型,你应该明确并在 Python 中要求 int :

i = int(12.34/3)

但是请记住, floor() 向负无穷大舍入而 int() 向零舍入,因此它们对负数给出不同的答案。因此,如果可能出现负值,您必须使用能够为您的应用程序提供所需结果的函数。

然而,Python 是一个不同的野兽,这是有充分理由的。它试图解决与 C 不同的问题集。 Python 的静态类型非常适合快速原型设计和开发,但是当使用一种类型的对象(如浮点数)测试代码时,它会产生一些非常复杂且难以发现的错误,传递 int 参数时,以微妙且难以找到的方式失败。正因为如此,我们为 Python 做出了许多有趣的选择,将意外错误最小化的需求置于其他历史规范之上。

更改除法以始终返回浮点数(或某种形式的非整数)是朝着正确方向迈出的一步。从同样的角度来看,使 // 成为一个 floor(a/b) 函数,而不是一个“整数除法”是合乎逻辑的。

使 float 除以零成为致命错误而不是返回 float("inf") 同样明智,因为在大多数 python 代码中,除以零不是数值计算而是编程错误,其中数学错误或存在是一个错误。对于普通的 Python 代码来说,更重要的是在错误发生时捕获该错误,而不是以“inf”的形式传播隐藏的错误,从而导致距离实际错误数英里的爆炸。

并且只要语言的其余部分在需要时将整数转换为浮点数做得很好,例如在除法或 math.sqrt() 中,让 math.floor() 返回一个整数是合乎逻辑的,因为如果以后需要它作为浮点数,它将正确转换回浮点数。如果程序员需要一个 int,那么这个函数就给了他们他们需要的东西。 math.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 是不同的,有充分的理由,理解它需要一点时间。在这种情况下,我们可以看到,不了解数值 floor() 函数历史的 OP,需要并期望它从他们对数学整数和实数的思考中返回一个 int。现在 Python 正在做我们的数学(与计算机科学)培训所暗示的那样。这使得它更有可能做初学者期望它做的事情,同时仍然用 NumPy 和 SciPy 覆盖高级数值算法的所有更复杂的需求。 Python 的演变过程给我留下了深刻的印象,即使有时我完全措手不及。