182

我发现了 Python 中的//运算符,它在 Python 3 中用地板除法。

有没有用 ceil 划分的运算符?(我知道/Python 3 中进行浮点除法的运算符。)

4

8 回答 8

373

不,但您可以使用倒置的楼层划分:¹

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

这是有效的,因为Python 的除法运算符执行地板除法(与 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!

一般来说,除非您特别需要,否则完全避免浮点运算被认为是一种很好的做法。浮点数学有几个棘手的边缘情况,如果您不密切注意,它们往往会引入错误。在没有硬件 FPU 的小型/低功耗设备上,它的计算成本也可能很高。


¹在此答案的先前版本中, ceildiv 被实施为,return -(-a // b)return -(a // -b)在评论者报告后者在基准测试中表现稍好后更改为。这是有道理的,因为被除数 ( a ) 通常大于除数 ( b )。由于 Python 使用任意精度算术来执行这些计算,因此计算一元否定-a几乎总是涉及与计算相同或更多的工作-b

于 2013-07-07T10:30:02.063 回答
73

没有与 ceil 相除的运算符。您需要import math并使用math.ceil

于 2013-02-11T22:35:29.347 回答
56

解决方案 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)每当存在非零余数时,步骤 with将商加一。

解决方案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()函数返回一个整数。

于 2019-02-08T02:06:29.453 回答
31

你可以(x + (d-1)) // d在除以时xd,例如(x + 4) // 5

于 2013-02-11T22:54:17.300 回答
22

你也可以随时内联

((foo - 1) // bar) + 1

在 python3 中,这比强制浮点除法和调用 ceil() 快一个数量级,前提是您关心速度。你不应该这样做,除非你已经通过使用证明你需要这样做。

>>> timeit.timeit("((5 - 1) // 4) + 1", number = 100000000)
1.7249219375662506
>>> timeit.timeit("ceil(5/4)", setup="from math import ceil", number = 100000000)
12.096064013894647
于 2013-02-11T23:03:45.540 回答
8

请注意,math.ceil 的精度限制为 53 位。如果您使用大整数,您可能无法得到准确的结果。

gmpy2提供了一个c_div使用上限舍入的函数。

免责声明:我维护 gmpy2。

于 2013-02-11T23:59:29.667 回答
-1

如果您想将一个数字取消多个。它就像我们在 excel 中有 Math.celling 一样工作。

def excel_celling(number=None, multiple_off=None):
    quotient = number // multiple_off
    reminder = number % multiple_off
    celling_value = quotient * multiple_off + (multiple_off, 0)[reminder==0]
    return int(celling_value)


assert excel_celling(99.99, 100) == 100, "True"
print(excel_celling(99.99, 100) , 100)
assert excel_celling(1, 100) == 100, "True"
print(excel_celling(1, 100),100)
assert excel_celling(99, 100) == 100, "True"
print(excel_celling(99, 100),100)
assert excel_celling(90, 100) == 100, "True"
print(excel_celling(90, 100),100)
assert excel_celling(101, 100) == 200, "True"
print(excel_celling(101, 100),200)
assert excel_celling(199, 100) == 200, "True"
print(excel_celling(199, 100),200)
assert excel_celling(199.99, 100) == 200, "True"
print(excel_celling(199.99, 100),200)
assert excel_celling(200, 100) == 200, "True"
print(excel_celling(200, 100),200)

结果

100 100

100 100

100 100

100 100

200 200

200 200

200 200

200 200

于 2021-05-16T07:38:04.427 回答
-12

简单的解决方案:a // b + 1

于 2020-01-25T16:36:22.383 回答