Python中是否有类似//运算符的上限操作符?

215 投票
9 回答
156214 浏览
提问于 2025-04-17 15:33

我发现了Python中的//运算符,它在Python 3中是用来进行向下取整的除法。

有没有一个运算符可以进行向上取整的除法呢?(我知道/运算符在Python 3中是用来进行浮点数除法的。)

9 个回答

67

解决方案 1:通过取反将向下取整转换为向上取整

def ceiling_division(n, d):
    return -(n // -d)

这个方法有点像Penn & Teller的悬浮魔术,它先把世界“颠倒过来(通过取反)”,然后使用普通的向下取整(把向上取整和向下取整的位置调换了),最后再把世界“翻正(再取反一次)”。

解决方案 2:让divmod()来处理

def ceiling_division(n, d):
    q, r = divmod(n, d)
    return q + bool(r)

divmod()这个函数会返回一个元组(a // b, a % b),用于整数(对于浮点数可能不太可靠,因为会有舍入误差)。在这个过程中,bool(r)这一步会在有非零余数时给商加一。

解决方案 3:在除法前调整分子

def ceiling_division(n, d):
    return (n + d - 1) // d

把分子向上调整,这样向下取整就能正确地向上取整。注意,这个方法只适用于整数。

解决方案 4:转换为浮点数以使用math.ceil()

def ceiling_division(n, d):
    return math.ceil(n / d)

math.ceil()这个代码很容易理解,但它会在整数和浮点数之间转换。这种方式速度不快,而且可能会有舍入问题。此外,它依赖于Python 3的语义,其中“真实除法”会产生浮点数,而ceil()函数会返回一个整数。

440

不可以,但你可以使用倒置的整除方法:¹

def ceildiv(a, b):
    return -(a // -b)

之所以这样做是因为Python的除法运算符会进行向下取整(和C语言不同,C语言的整数除法会直接去掉小数部分)。

这里有个示范:

>>> from __future__ import division     # for Python 2.x compatibility
>>> import math
>>> def ceildiv(a, b):
...     return -(a // -b)
...
>>> b = 3
>>> for a in range(-7, 8):
...     q1 = math.ceil(a / b)   # a/b is float division
...     q2 = ceildiv(a, b)
...     print("%2d/%d %2d %2d" % (a, b, q1, q2))
...
-7/3 -2 -2
-6/3 -2 -2
-5/3 -1 -1
-4/3 -1 -1
-3/3 -1 -1
-2/3  0  0
-1/3  0  0
 0/3  0  0
 1/3  1  1
 2/3  1  1
 3/3  1  1
 4/3  2  2
 5/3  2  2
 6/3  2  2
 7/3  3  3

为什么用这个而不是math.ceil?

math.ceil(a / b)可能会悄悄地产生错误的结果,因为它会引入浮点数误差。例如:

>>> from __future__ import division     # Python 2.x compat
>>> import math
>>> def ceildiv(a, b):
...     return -(a // -b)
...
>>> x = 2**64
>>> y = 2**48
>>> ceildiv(x, y)
65536
>>> ceildiv(x + 1, y)
65537                       # Correct
>>> math.ceil(x / y)
65536
>>> math.ceil((x + 1) / y)
65536                       # Incorrect!

一般来说,除非你特别需要,否则最好避免使用浮点数运算。浮点数运算有很多棘手的边界情况,如果你不仔细的话,容易引入错误。而且在一些小型或低功耗的设备上,浮点运算可能会比较耗费计算资源,因为这些设备可能没有专门的硬件浮点单元。


¹在这个回答的早期版本中,ceildiv是用return -(-a // b)实现的,但后来评论者反馈说用return -(a // -b)的性能稍微好一些,所以进行了修改。这是有道理的,因为被除数(a)通常比除数(b)大。由于Python使用任意精度的算术来进行这些计算,计算一元负号-a通常会比计算-b需要更多的工作量。

72

没有直接可以用来向上取整的除法运算符。你需要先 import math,然后使用 math.ceil 这个函数。

撰写回答