classification
Title: Ceil division with /// operator
Type: enhancement Stage: resolved
Components: Library (Lib) Versions: Python 3.10
process
Status: closed Resolution: rejected
Dependencies: Superseder:
Assigned To: Nosy List: PedanticHacker, mark.dickinson, rhettinger, serhiy.storchaka, tim.peters, vstinner
Priority: normal Keywords:

Created on 2021-02-18 14:18 by PedanticHacker, last changed 2021-02-19 15:46 by gvanrossum. This issue is now closed.

Messages (10)
msg387231 - (view) Author: Boštjan Mejak (PedanticHacker) * Date: 2021-02-18 14:18
I hope I'm not too late for a Python 3.10 feature request. I love the fact that Python 3.x does floor division with the // operator.

We, however, don't have a ceil division operator, besides importing the math module and using its math.ceil() function.

Is it possible we have /// as a ceil division operator in Python 3.10?

I just wanted to put this thought out of my head if anyone becomes entertained by this idea.
msg387236 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2021-02-18 14:50
Since adding new operator is a syntax-level change, it needs a PEP. It is better to start with discussing on the Python-ideas mailing list (https://mail.python.org/mailman3/lists/python-ideas.python.org/). Let's see if this idea will be accepted favorably.

BTW, the ceiling division can be expressed as -(-a // b) or -(a // -b).
msg387237 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2021-02-18 15:00
I'm not convinced that this operation is so common that it deserves a new operator. As Serhiy wrote, it must be first on python-ideas first. I close the issue.

--

floor division is x//y

integer ceil division can be implemented with math.ceil(x/y) for small numbers.

For large integer numbers, I like to use something like:

def ceil_div(x, y):
    rem=x % y
    if rem:
        x += y - rem
    return x//y

I let you adjust it for negative numbers ;-)
msg387248 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2021-02-18 17:21
I can't recall where I saw this originally, but you can use the spaceship operator "--0--" to turn floor division into ceiling division:

>>> 12//5
2
>>> --0-- 12//5
3

There's also the ++0++ operator when you want to emphasize that you really *do* mean floor division:

>>> ++0++ 12//5
2

But yes, -(-n // d) is the easy (if a little bit cryptic) way to get the ceiling of n / d.
msg387264 - (view) Author: Boštjan Mejak (PedanticHacker) * Date: 2021-02-18 18:42
The spaceship operator is kinda cool! :)

Well, I was hoping to avoid importing the math module just to have ceil division capabilities.

Anyway, my need for ceiling is that I am coding a chess GUI where I have a table with 2 columns and I create a new row whenever White makes a move. So, my implementation for row creation is math.ceil(len(moves) // 2) where moves is a list of chess moves. 

So, the sequence of table rows after White and Black make their moves is initially 0 (since there are no moves yet) and then 1 (first row created after White made a move) and then 1 (again on row 1 after Black made a move, but on column 2), then 2 2 3 3 4 4 5 5 6 6 and so on.

If we had the ceil operator in Python, I could do: len(moves) /// 2 :)

By the way -- do you happen to know any better idea to get that kind of a sequence? That is 0 1 1 2 2 3 3 4 4 5 5 6 6 and so on.
msg387267 - (view) Author: Tim Peters (tim.peters) * (Python committer) Date: 2021-02-18 19:02
(len(moves) + 1) // 2
msg387268 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2021-02-18 19:08
> Anyway, my need for ceiling is that I am coding a chess GUI where I have a table with 2 columns and I create a new row whenever White makes a move. So, my implementation for row creation is math.ceil(len(moves) // 2) where moves is a list of chess moves. 

You may like the new int.bit_count() attribute of Python 3.10 ;-)
https://docs.python.org/dev/library/stdtypes.html#int.bit_count
msg387269 - (view) Author: Boštjan Mejak (PedanticHacker) * Date: 2021-02-18 20:41
My hat off to you, Mr. Peters! Your suggestion (len(moves) + 1) // 2 works perfectly and is much more elegant than --0-- len(moves) // 2. :)

Mr. Stinner, in what way would int.bit_count() be beneficial to me?
msg387270 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2021-02-18 21:12
> Mr. Stinner, in what way would int.bit_count() be beneficial to me?

I found https://www.chessprogramming.org/Population_Count when I investigated the C implementation of _Py_popcount32(), which is used by int.bit_count().

I read it when I tried to document this surprising code:
---
x = x - ((x >> 1) & 0x55555555);
x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
x = (x + (x >> 4)) & 0x0F0F0F0F;
return (uint32_t)((uint64_t)x * (uint64_t)0x01010101) >> 24;
---

I reformatted it as:
---
    // 32-bit SWAR (SIMD Within A Register) popcount

    // Binary: 0 1 0 1 ...
    const uint32_t M1 = 0x55555555;
    // Binary: 00 11 00 11. ..
    const uint32_t M2 = 0x33333333;
    // Binary: 0000 1111 0000 1111 ...
    const uint32_t M4 = 0x0F0F0F0F;
    // 256**4 + 256**3 + 256**2 + 256**1
    const uint32_t SUM = 0x01010101;

    // Put count of each 2 bits into those 2 bits
    x = x - ((x >> 1) & M1);
    // Put count of each 4 bits into those 4 bits
    x = (x & M2) + ((x >> 2) & M2);
    // Put count of each 8 bits into those 8 bits
    x = (x + (x >> 4)) & M4;
    // Sum of the 4 byte counts
    return (uint32_t)((uint64_t)x * (uint64_t)SUM) >> 24;
---
msg387325 - (view) Author: Boštjan Mejak (PedanticHacker) * Date: 2021-02-19 14:35
Mr. Stinner, I really don't understand what are you hinting at.
History
Date User Action Args
2021-02-19 15:46:13gvanrossumsetnosy: - gvanrossum
2021-02-19 14:35:26PedanticHackersetmessages: + msg387325
2021-02-18 21:12:34vstinnersetmessages: + msg387270
2021-02-18 20:41:28PedanticHackersetmessages: + msg387269
2021-02-18 19:08:47vstinnersetmessages: + msg387268
2021-02-18 19:02:07tim.peterssetnosy: + tim.peters
messages: + msg387267
2021-02-18 18:42:49PedanticHackersetmessages: + msg387264
2021-02-18 17:46:46mark.dickinsonsetresolution: not a bug -> rejected
2021-02-18 17:21:42mark.dickinsonsetnosy: + mark.dickinson
messages: + msg387248
2021-02-18 15:00:41vstinnersetstatus: open -> closed

nosy: + vstinner
messages: + msg387237

resolution: not a bug
stage: resolved
2021-02-18 14:50:17serhiy.storchakasetnosy: + serhiy.storchaka
messages: + msg387236
2021-02-18 14:18:04PedanticHackercreate