9 ms·
Why Python's integer division floors (2010)
- aimor 3y agoWhat do you do in Python when you want different behavior? Sometimes it's desirable to fix, floor, ceil, or round depending on the situation.
- wayvey 3y agoYou convert to float, divide and then do floor, ceil or whatever.
- eugenekolo 3y ago`int(a/b)` instead of `a//b` (for trunc to zero behavior. especially important when working with c or disassembled code)
- orlp 3y agoAssuming a, b are integers, the following answers are exact: def div_floor(a, b): return a // b def div_ceil(a, b): return (a + b - 1) // b def div_trunc(a, b): return a // b if (a < 0) == (b < 0) else -(-a // b) def div_round(a, b): return (2*a + b) // (2*b)
- taeric 3y agoI confess I have grown increasingly in favor of how common lisp does this. The basic `/` creates rationals. And "floor" is a multiple value return where the first value is the floor and the second is the remainder. Granted, getting used to multiple value calls takes getting used to. I think I like it, but I also think I emphatically did not like it initially.
- mik1998 3y agoIt's nice for general numerical methods but somewhat annoying for implementing integer-arithmetic algorithms.
- taeric 3y agoYeah, I think this is what I really didn't like about it at the start. Learning that you probably want `floor` if you want things to be integer took more time to learn than feels natural.
- lalaithion 3y agoTo be fair, Python's `/` creates floats, and this article is about a second operator `//`.
- taeric 3y agoContinuing in fairness, lisp is a bit more involved, here. If you do `(/ 1.0 3)`, you do not get a rational. Similarly, any division that is not integer/rational there will get treated mostly as expected. Basically, it seems as soon as you introduce a floating point number, it stays there. Which is roughly what I would expect.
- im3w1l 3y agoIn python 2, '/' creates integers, and the article was written in 2010 when python 2 was the most used by far.
- lalaithion 3y agofrom the link: > PS. Note that I am using // instead of / -- this is Python 3 syntax
- im3w1l 3y agoOh. Right.
- shawn_w 3y agoScheme in the form of R7RS does something similar, though since failure to capture all returned values is an error (unlike in Common Lisp), there's more functions involved. It also offers a choice between truncation and floor behavior, so there's `truncate-quotient`, `truncate-remainder`, `floor-` versions and for both values `truncate/` and `floor/`.
- dang 3y agoDiscussed at the time: Why Python's Integer Division Floors - https://news.ycombinator.com/item?id=1630394 https://news.ycombinator.com/item?id=1630394 - Aug 2010 (2 comments)
- vsuperpower2020 3y agoSo no good reasons, just mathematical ones.
- z_open 3y agoChanging 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.
- lifthrasiir 3y agoQuantify "well known". Historically enough variation existed in this area [1], and C only happened to copy FORTRAN's behavior for the sake of compatibility. [1] https://en.wikipedia.org/wiki/Modulo#In_programming_languages https://en.wikipedia.org/wiki/Modulo#In_programming_language...
- BlueTemplar 3y agoPython does follow the convention, but what I am wondering now is why did FORTRAN break it ?
- asplake 3y agoFortran is old – 1958 onwards. It has precedence here, though at what point it separated the two behaviours into mod and modulo functions I don’t know. Edit: From what I can tell, standardised in Fortran 90, presumably older than that.
- BlueTemplar 3y agoMy point is that Fortran doesn't have precedence on math, see this comment by Austin Feller : http://python-history.blogspot.com/2010/08/why-pythons-integer-division-floors.html?showComment=1404050838672#c3832102777815381946 http://python-history.blogspot.com/2010/08/why-pythons-integ...
- wombatpm 3y agoMaybe because FORTRAN arrays index from 1 by default?
- Skeime 3y ago
- pansa2 3y agoNote 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.
- Skeime 3y agoI 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 integers. I also just learned that integer division // can be applied to floats in Python, but the result is not an integer, for some reason?)
- kragen 3y agoto 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
- extraduder_ire 3y ago> 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.
- notso411 3y agoHow many integer 2s go in to 7? 3 not 4.. Not a hard question to answer.
- OscarCunningham 3y agoYes that makes perfect sense. So why is int(-1.5) == -1? It should be -2.
- mmcnickle 3y agoThe same reason int(-1.999) is -1; The operation is different to integer division. I think of it as taking the "integer" part of the float.
- Izkata 3y agoAs the article mentions, this is called truncation. It truncates (cuts off) the value at the decimal point.
- consp 3y agobecause 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
- cygx 3y agoIt'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.
- odyssey7 3y agoInspired by Monty Python, the surprises are part of the charm. ¯\_(ツ)_/¯
- Skeime 3y agoThere is math.floor for rounding towards negative infinity, which has the advantage of being crystal clear.
- nwellnhof 3y agoIt 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.
- kragen 3y agomentioned 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
- DonHopkins 3y agoWho would have ever thought that division could be so ... divisive?
- kragen 3y agoas sophie w. arm said, 'i have a multiplier, not a divider'
- DonHopkins 3y agoAt the RISC of sounding short on instructions, do you mean Sophie Wilson's ARM? ;) https://en.wikipedia.org/wiki/Sophie_Wilson https://en.wikipedia.org/wiki/Sophie_Wilson
- Findecanor 3y agoOne 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.
- DonHopkins 3y agoForth-83 beat C99 by 16 years! >Python, unlike C, has the mod operator always return a positive number (twitter.com/id_aa_carmack): https://news.ycombinator.com/item?id=29729890 https://news.ycombinator.com/item?id=29729890 https://twitter.com/ID_AA_Carmack/status/1476294133975240712 https://twitter.com/ID_AA_Carmack/status/1476294133975240712 tzs on Dec 30, 2021 | next [–] >The submission is a tweet which doesn't really have a title, so the submitter was forced to make up one. Unfortunately the assertion in the chosen title is not correct. In Python the mod operator returns a number with the same sign as the second argument: >>> 10%3 1 >>> (-10)%3 2 >>> 10%(-3) -2 >>> (-10)%(-3) -1 https://news.ycombinator.com/item?id=29732335 https://news.ycombinator.com/item?id=29732335 DonHopkins on Dec 30, 2021 | parent | context | favorite | on: Python, unlike C, has the mod operator always retu... The FORTH-83 standard adopted floored division. Signed Integer Division, by Robert L. Smith. Originally appearing in Dr. Dobb's Journal September 1983: https://wiki.forth-ev.de/doku.php/projects:signed_integer_division https://wiki.forth-ev.de/doku.php/projects:signed_integer_di... Lots of other languages got it wrong: https://en.wikipedia.org/wiki/Modulo_operation#In_programming_languages https://en.wikipedia.org/wiki/Modulo_operation#In_programmin... Symmetric division is the kind of thing that causes rockets ships to explode unexpectedly. Symmetric division considered harmful: https://www.nimblemachines.com/symmetric-division-considered-harmful/ https://www.nimblemachines.com/symmetric-division-considered... >Since its 1983 standard (Forth-83), Forth has implemented floored division as standard. Interestingly, almost all processor architectures natively implement symmetric division. >What is the difference between the two types? In floored division, the quotient is truncated toward minus infinity (remember, this is integer division we’re talking about). In symmetric division, the quotient is truncated toward zero, which means that depending on the sign of the dividend, the quotient can be truncated in different directions. This is the source of its evil. >I’ve thought about this a lot and have come to the conclusion that symmetric division should be considered harmful. >There are two reasons that I think this: symmetric division yields results different from arithmetic right shifts (which floor), and both the quotient and remainder have singularities around zero. >If you’re interested in the (gory) details, read on. [...]
- Aardwolf 3y agoThey made the correct decision here! Too bad they made the wrong decision in not supporting the expected floating point behavior of division through zero (python throwing errors, rather than return inf or nan)
- kragen 3y agoin https://stackoverflow.com/questions/78064239/does-python-not-follow-ieee-754-in-case-of-division-by-zero https://stackoverflow.com/questions/78064239/does-python-not... it seems like ieee 754 does permit throwing errors on division by zero, and although it isn't the default behavior on any hardware i've used, the name of unix's division-by-zero exception signal strongly suggests that it's also the default behavior on the (non-ieee-754-compliant) pdp-11. https://stackoverflow.com/questions/12954193/why-does-division-by-zero-with-floating-point-or-double-precision-numbers-not https://stackoverflow.com/questions/12954193/why-does-divisi... makes the opposite assertion, though that ieee 754 does not permit division by zero traps. i'm not sure what to believe people commonly opt out of python's behavior in this case by using numpy python's decision is maybe suboptimal for efficient compilation, but it has a lot of decisions like that edit: downthread https://news.ycombinator.com/item?id=39540416 https://news.ycombinator.com/item?id=39540416 pclmulqdq clarifies that in fact trapping on division by zero is not only ieee-754-compliant but (unlike flooring integer division!) efficiently implementable on common high-performance architectures
- pclmulqdq 3y agoIEEE 754 has an infinity, so division by zero isn't the catastrophic thing that it is in integer. However, division by zero is still an exception as defined by the 754 standard. What hardware does with these exceptions is a separate question, though. Some CPUs will swallow them for performance.
- kragen 3y agothere's a whole set of terminology about 'exceptions', 'traps', and 'signaling' in ieee 754 that i don't really understand, but in particular the second thread i linked there seems to claim that an ieee 754 'exception' is almost, but not quite, completely unlike a python 'exception', which it apparently calls a 'trap' (as do many cpu architectures): > When exceptional situations need attention, they can be examined immediately via traps or at a convenient time via status flags. Traps can be used to stop a program, but unrecoverable situations are extremely rare. you know quite a bit about cpus. do you know of any ieee-754 cpus that trap on floating-point division by zero by default? is there a way to configure commonly-used cpus to do so?