Python中是否有类似//运算符的上限操作符?
我发现了Python中的//
运算符,它在Python 3中是用来进行向下取整的除法。
有没有一个运算符可以进行向上取整的除法呢?(我知道/
运算符在Python 3中是用来进行浮点数除法的。)
9 个回答
解决方案 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()函数会返回一个整数。
不可以,但你可以使用倒置的整除方法:¹
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
需要更多的工作量。
没有直接可以用来向上取整的除法运算符。你需要先 import math
,然后使用 math.ceil
这个函数。