It’s one of those tricky topics where on its own the answer to “Should we group the existing discrete arithmetic functions into a new dedicated standard library module?” is fairly obviously “No, it’s too much work for not enough practical benefit” (thus making a standalone discussion very short).
However, at some point, some proposal is going to have to hit the threshold of “OK, this is getting silly, we need a dedicated place to access and document these, separate from math and the builtin namespace”. Until then, we’re going to have the “math isn’t the right place, but it’s the best option we have readily available” discussion each time.
I guess a dedicated thread would at least provide a place to summarise the full list of relevant APIs that already exist in the stdlib (using @madeleineth’s list in Integer ceiling divide - #13 by madeleineth as a starting point).
As a datapoint on my claim that codebases tend to end up with multiple copies of the function, I found three variations in cpython (2/3 on the C side), two of them confusing enough that the authors felt the need to comment what the expression meant.
If ceildiv() is added to stdlib, we should add also a function for division with rounding to the nearest integer (like _pydatetime._divide_and_round() or _PyLong_DivmodNear()). It may be useful in more cases than rounding up.
The initial gcd() implementation was in the fractions module. It contains also an implementation for division with rounding to the nearest integer, but not as a public function.
Now that math.integer will be implemented in Python 3.15, perhaps we can continue this thread.
Question:
If mod() gives the remainder of floordiv(), should we consider ceilmod() for the spares of ceildiv()? I.e. for the ‘x objects and containers size y’ of OP, ceildiv(x, y) gives the number of containers and ceilmod(x, y) tells how many empty slots remain.
def ceildiv(m, n): return -(m // -n)
def ceilmod(m, n): return -(m % -n)
for m in tests:
for n in tests:
assert n == 0 or ceildiv(m, n) * n - ceilmod(m, n) == m, "new invariant"
assert n == 0 or floordiv(m, n) * n + mod(m, n) == m, "regular invariant"
Orthogonality would suggest so, but this path risks growth with ceildivmod(), roundmod(), and rounddivmod().
OTOH, Java has math.ceilMod() with an opposite sign:
def ceildiv(m, n): return -(m // -n)
def ceilmod(m, n): return (m % -n)
for m in tests:
for n in tests:
assert n == 0 or ceildiv(m, n) * n + ceilmod(m, n) == m, "same invariant as floor"
assert n == 0 or floordiv(m, n) * n + mod(m, n) == m, "regular invariant"
To avoid confusion, following this definition seems advisable (even if positive operands then give negative outcomes).