Live data from Hacker News

Why Python's integer division floors (2010)

python-history.blogspot.com

21–30 of 102 posts

Re: Why Python's integer division floors (2010)

#21
post #2

Changing well known behavior for something no one is really going to need. The justification makes sense, but it breaks convention and the relationship with modulo doesn't need to hold for negative numbers.

> the relationship with modulo doesn't need to hold for negative numbers.

It especially needs to hold, given how often overlooked negative numbers are when reasoning about programs.

Re: Why Python's integer division floors (2010)

#22

It should be noted that in C89, both behaviors of division and modulo are allowed and it's implementation-defined whether division results are truncated or rounded towards negative infinity. This has changed in C99 which only allows truncation towards zero.

mentioned in the comments on the post, which merit reading in this case

i haven't read the rationale, but presumably the committee did this because it's what virtually all cpus do (because it's what fortran does), so it's the only thing that can be implemented efficiently on virtually any cpu with a division instruction, and so virtually all c implementations did it, and standardizing the behavior of virtually all c implementations is better than leaving it implementation-defined or standardizing a behavior that conflicts with virtually all existing implementations and can't be implemented efficiently on most high-end hardware

Re: Why Python's integer division floors (2010)

#23
post #4
post #2

Changing well known behavior for something no one is really going to need. The justification makes sense, but it breaks convention and the relationship with modulo doesn't need to hold for negative numbers.

I strongly disagree. I would estimate that in 90% of cases where I use modulo in languages that truncate (instead of flooring), I write (a % b + b) % b, or something similar, just to get the right behaviour. The exceptional cases are those where I can convince myself that negative numbers simply won't come up. (It's never because I actually want the other behaviour for negative numbers.) - When using modulo to access…

> I have never seen a case where truncation was the right thing to do.

Splitting a quantity into units of differing orders of magnitude. For example, −144 minutes is −2 hours and −24 minutes, not −3 hours and 36 minutes. This is about the only case I know of, though.

Re: Why Python's integer division floors (2010)

#24
post #4

Earlier quoted context omitted.

I strongly disagree. I would estimate that in 90% of cases where I use modulo in languages that truncate (instead of flooring), I write (a % b + b) % b, or something similar, just to get the right behaviour. The exceptional cases are those where I can convince myself that negative numbers simply won't come up. (It's never because I actually want the other behaviour for negative numbers.) - When using modulo to access…

> I have never seen a case where truncation was the right thing to do. Splitting a quantity into units of differing orders of magnitude. For example, −144 minutes is −2 hours and −24 minutes, not −3 hours and 36 minutes. This is about the only case I know of, though.

In generic numerics work there are two broad cases when you have a sea of sample points (eg: a 2D plane with samples taken all over) and you cast a mesh across the sea to divide the work into cells.

\1 samples have new coords relative to cell left edge and bottom edge.

    We're interested in sample distance from "classic" first quadrant axis.
\2 samples are weighted by distance from cell centre.

    We're pooling, combining channel layers, making representative central 'blobs', we're interested in cell sample positions relative to the centre point of the cell.
Your example is a good one, and falls in the case 2 grouping.

This, of course, generalises to 3D and higher dimensions.

Re: Why Python's integer division floors (2010)

#25
post #5

Note the top comment by “ark” - there’s really no perfect solution here. In the floating-point case, you have to choose between negative remainders or potentially inexact results. And you definitely want integer division to work the same as float division.

Python's floating point flooring division operator also has a pitfall: it gives a correctly rounded result as if you computed (a / b) with infinite precision before flooring. This can lead to the following:

    >>> 1 / 0.1
    10.0
    >>> 1 // 0.1
    9.0
This is because 0.1 is in actuality the floating point value value 0.1000000000000000055511151231257827021181583404541015625, and thus 1 divided by it is ever so slightly smaller than 10. Nevertheless, fpround(1 / fpround(1 / 10)) = 10 exactly.

I found out about this recently because in Polars I defined a // b for floats to be (a / b).floor(), which does return 10 for this computation. Since Python's correctly-rounded division is rather expensive, I chose to stick to this (more context: https://github.com/pola-rs/polars/issues/14596#issuecomment-...).

Re: Why Python's integer division floors (2010)

#26
post #14
post #5

Note the top comment by “ark” - there’s really no perfect solution here. In the floating-point case, you have to choose between negative remainders or potentially inexact results. And you definitely want integer division to work the same as float division.

I mean "inexact results" is essentially float's life motto. That said, I don't really see why you would necessarily want float and integer division to behave the same. They're completely different types used for completely different things. Pick the one that is appropriate for your use case. (It seems like abs(a % b) <= abs(b / 2) might be the right choice for floats which is pretty clearly not what you want for inte…

to people who use floating-point math seriously, it's very important for floating-point results to be predictably inexact; if they aren't, floating point is at best useless and usually harmful

i also didn't know python supported // on floats

Re: Why Python's integer division floors (2010)

#27
post #2

Changing well known behavior for something no one is really going to need. The justification makes sense, but it breaks convention and the relationship with modulo doesn't need to hold for negative numbers.

> Changing well known behavior for something no one is really going to need. On the contrary I can't imagine when and why anybody would want truncation. That's just a side effect of the used algorithm and not something that actually makes much (any?) sense.

the post gives two examples:

- given a number t of seconds after the epoch, what time of day does it represent? using python's definition, (t + tz) % 86400

- given an offset d between two pixels in a pixel buffer organized into sequential lines, what is the x component of the offset? using python's definition, d % width is right when the answer is positive, width - (d % width) when the answer is negative, so you could write d % width - d > p0x ? d % width : width - d % width. this gets more complicated with the fortran definition, not simpler

edit: the correct expression is (d % width if p0x + d % width http://canonical.org/~kragen/sw/dev3/modpix.py

Re: Why Python's integer division floors (2010)

#28
post #20
post #13

Earlier quoted context omitted.

because it's a float and not an int, it's strictly talking about integers here, not floats. All int conversions of floats are converted by discarding the remainder afaik but I could be wrong here (e.g. int(1.9) == 1, and int(-1.9) == -1) edit: -3//2 == -2 for instance, since it's strictly integer division

It's a valid criticism: By the principle of least surprise, one should strive for a//b = int(a/b). Basically, there's no free lunch. Personally, I prefer truncating integer division in combination with a pair of remainder operators.

Inspired by Monty Python, the surprises are part of the charm. ¯\_(ツ)_/¯

Re: Why Python's integer division floors (2010)

#29

It should be noted that in C89, both behaviors of division and modulo are allowed and it's implementation-defined whether division results are truncated or rounded towards negative infinity. This has changed in C99 which only allows truncation towards zero.

One thing that is still "implementation-defined" in C and C++ is the result of a right shift of a negative integer. On pretty much all platforms, the >> operator shifts in the sign bit and does not round the result — which makes it equivalent to flooring division by a power of two. It is consistent with the division operator only when the left-hand value is positive.

Re: Why Python's integer division floors (2010)

#30
post #26
post #14

Earlier quoted context omitted.

I mean "inexact results" is essentially float's life motto. That said, I don't really see why you would necessarily want float and integer division to behave the same. They're completely different types used for completely different things. Pick the one that is appropriate for your use case. (It seems like abs(a % b) <= abs(b / 2) might be the right choice for floats which is pretty clearly not what you want for inte…

to people who use floating-point math seriously, it's very important for floating-point results to be predictably inexact; if they aren't, floating point is at best useless and usually harmful i also didn't know python supported // on floats

> i also didn't know python supported // on floats

Like most surprising features in python, it would be terribly annoying if it didn't. Especially since you couldn't make sure the argument to the function you're writing wasn't a float. At that time, anyway.

Post reply on HN