[HN Gopher] Why Python's integer division floors (2010)
___________________________________________________________________
Why Python's integer division floors (2010)
Author : bshanks
Score : 95 points
Date : 2024-02-26 23:52 UTC (1 days ago)
(HTM) web link (python-history.blogspot.com)
(TXT) w3m dump (python-history.blogspot.com)
| z_open wrote:
| 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.
| lifthrasiir wrote:
| Quantify "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_language...
| BlueTemplar wrote:
| Python does follow the convention, but what I am wondering
| now is why did FORTRAN break it ?
| asplake wrote:
| Fortran 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 wrote:
| My 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-
| integ...
| wombatpm wrote:
| Maybe because FORTRAN arrays index from 1 by default?
| Skeime wrote:
| 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 an array cyclically. (You might
| get lucky that your language allows using negative numbers to
| index from the back. In that case, both conventions work.) -
| When lowering the resolution of integers. If you round to zero,
| you get strange artifacts around zero, because -b+1, ..., -1,
| 0, 1, ..., b-1 all go to zero when dividing by b. That's 2b-1
| numbers. For every other integer k, there are only b numbers
| (namely bk, bk+1, ..., bk+b-1).
|
| I have _never_ seen a case where truncation was the right thing
| to do. (When dealing with integers. Floats are different, of
| course, but they are not what this is about.)
| ncruces wrote:
| Very much this.
| cygx wrote:
| A common approach (e.g. Cobol, Ada, Common Lisp, Haskell,
| Clojure, MATLAB, Julia, Kotlin) seems to be to provide two
| operators: One that uses truncated division, one that uses
| floored division. By convention, _rem_ truncates, and _mod_
| floors.
| pwdisswordfishc wrote:
| > 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.
| defrost wrote:
| 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.
| Skeime wrote:
| Oh, that's a somewhat reasonable one. (Though I would
| expect that you actually want -(2 hours and 24 minutes) in
| many cases instead, thus needing to handle the negative
| case separately anyway.)
| ReleaseCandidat wrote:
| > 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.
| kragen wrote:
| 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
| < width else d % width - width) or in c-like syntax (p0x + d
| % width < width ? d % width : d % width - width). see
| http://canonical.org/~kragen/sw/dev3/modpix.py
| pwdisswordfishc wrote:
| > 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.
| pansa2 wrote:
| 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.
| Skeime wrote:
| 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
| 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 wrote:
| 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
| extraduder_ire wrote:
| > 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.
| kragen wrote:
| int(x) // int(y)
| Skeime wrote:
| This does something different. a // b tries to "fit" b
| into a as many times as possible. For example, 1.0 // 0.4
| == 2.0 because 0.4 fits twice into 1.0 (with a remainder
| of 0.2). Though as the result should always be an
| integer, I'd argue that the result should actually be 2,
| not 2.0. But alas, it's not.
|
| With your change, you calculate 1 // 0 instead, which
| crashes.
|
| That said, I think checking isinstance(x, float) was
| always possible. (And nowadays, you can put a type
| annotation.)
| kragen wrote:
| yes, agreed
| Skeime wrote:
| Sure, but the inexactness of this modulus operation would
| not be any more unpredictable than all other kinds of float
| inexactness. Unless you're talking about the case where you
| don't know whether your operands are ints or floats. But in
| that case, there are tons of other unpredictabilities. For
| example, a + b will round when adding (sufficiently large)
| integers-represented-as-floats while it will never round
| for integers. So if you take floating-point math seriously,
| knowing that you're actually dealing with floats is the
| first step.
| kragen wrote:
| it's true that it would be deterministic, but it would
| behave differently from the ieee 754 standard, which is
| at least surprising, which is another sense of the word
| 'unpredictable'. admittedly, floating-point math that is
| inexact in a _surprising_ way is not necessarily useless,
| and to someone who isn 't deep into numerical analysis,
| all floating-point math is inexact in surprising ways
|
| still, it would have real pitfalls if numerical
| algorithms that give right answers in every other
| programming language gave subtly wrong answers in python
| (raising a division-by-zero exception is less
| troublesome)
| orlp wrote:
| 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-...).
| pclmulqdq wrote:
| In an abstract sense, floating-point division is a
| fundamentally different operation than integer division. There
| is not generally expected to be a remainder fron floating-point
| division aside from 0.5 ULP that will be rounded away.
|
| If you use it without considering rounding modes carefully and
| then round to integer, you can get some funny results.
| notso411 wrote:
| How many integer 2s go in to 7?
|
| 3 not 4..
|
| Not a hard question to answer.
| Eiim wrote:
| Did you read the article? It's about what happens in negative
| numbers. Of course everyone agrees that 7/2=3 in integer
| division, but -7/2 is less obvious.
| OscarCunningham wrote:
| Yes that makes perfect sense. So why is int(-1.5) == -1? It
| should be -2.
| mmcnickle wrote:
| The 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 wrote:
| As the article mentions, this is called truncation. It
| truncates (cuts off) the value at the decimal point.
| consp wrote:
| 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
| cygx wrote:
| 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.
| odyssey7 wrote:
| Inspired by Monty Python, the surprises are part of the
| charm. -\\_(tsu)_/-
| Skeime wrote:
| There is math.floor for rounding towards negative infinity,
| which has the advantage of being crystal clear.
| nwellnhof wrote:
| 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.
| kragen wrote:
| 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
| DonHopkins wrote:
| Who would have ever thought that division could be so ...
| divisive?
| kragen wrote:
| as sophie w. arm said, 'i have a multiplier, not a divider'
| DonHopkins wrote:
| At the RISC of sounding short on instructions, do you
| mean Sophie Wilson's ARM? ;)
|
| https://en.wikipedia.org/wiki/Sophie_Wilson
| Findecanor wrote:
| 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.
| kazinator wrote:
| However, the oposite shift is undefined if a 1 goes into the
| sign bit.
|
| More precisely, regarding E1 << E2, it is written (in the
| April 2023 ISO C draft):
|
| "If E1 has a signed type and nonnegative value, and E1 x 22
| is representable in the result type, then that is the
| resulting value; otherwise, the behavior is undefined."
|
| Thus if E1 is negative, or if the result overflows, UB.
| rcfox wrote:
| Didn't they specify two's complement signed integers
| somewhat recently? What's the rationale for leaving these
| behaviours undefined?
| kazinator wrote:
| Because the shift operations are arithmetically defined,
| and the situations that are undefined correspond to
| overflow. v << 1 means the same thing as v * 2 and is
| undefined if v * 2 is undefined.
| Karellen wrote:
| I've always considered it a mistake to refer to "%" as a
| "modulo" operator in C because of its ability to return
| negative numbers. That's not how modulo arithmetic works. C's
| "%" is a _remainder_ operator.
| pwdisswordfishc wrote:
| Exactly. Modulo is not an operator, but a term for an
| equivalence class of differing by a multiple of a number
| fixed in advance.
| mathgradthrow wrote:
| That's an operator. A map to the quotient.
| Karellen wrote:
| > Modulo is not an operator,
|
| "operator" is being used as a term of art in programming
| language design, to describe a symbol used to indicate that
| an operation should be performed on one or more operands.
|
| https://en.wikipedia.org/wiki/Operator_(computer_programmin
| g...
|
| So the "%" symbol is an operator in C and Python, and it is
| standard usage to describe the operator that performs a
| mathematical function "X" as the "X operator".
| marko_oktabyr wrote:
| As sibling comments have mentioned, "operator" here is
| being used in the programming language sense of the word,
| not the mathematical sense. Using the name "mod" [as in
| mod(a,n)] or "modulo" for the mapping that takes integers
| to a particular representative of its equivalence class in
| Z/nZ ("integers modulo n") doesn't seem like an
| unreasonable choice to me.
| kazinator wrote:
| (mod N) is an operator which indicates that the arithmetic
| in the preceding formula or equation is understood as
| taking place in a modulo N congruence.
|
| A triple equal sign is usually used for modular equations.
| That triple equal sign, together with the (mod N) out to
| the right, constitute an operator.
|
| -1 [?] 9 (mod 10)
|
| Since it is written as part of the syntax of a formula or
| equation, and modifies its semantics, it is an operator.
| Just like d/dx, sigma notation and whatever not.
| matsemann wrote:
| I have to re-learn this each year for Advent of Code, when
| inevitable my something % anything goes into the negative
| when searching downwards.
| marko_oktabyr wrote:
| > That's not how modulo arithmetic works.
|
| We should be a bit careful with what one means by "modulo
| arithmetic" here. If we are talking about arithmetic in Z/nZ
| (read "integers modulo n"), then the objects being acted upon
| are no longer integers, but _equivalence classes_ of
| integers. That is, the set of all integers that satisfy the
| equivalence relation "~" where "a ~ b" means a - b = k*n for
| some integer k. For example, one of the equivalence classes
| in Z/3Z would be the set {..., -4, -1, 2, 5, ...}.
|
| Since every element of the set is equivalent under that
| relation, we typically will choose a _representative_ of that
| equivalence class, e.g., [2] for the previous class. If we
| view the "mod" or "modulo" operator to be a mapping from the
| integers to a particular representative of its equivalence
| class, there's no reason that negative values should be
| excluded. [-1] refers to the same equivalence class as [2],
| [32], and [-7]. The details of how integers are mapped to a
| chosen representative seem to vary from language to language,
| but modular arithmetic works all the same between them.
| kazinator wrote:
| Under modulo math, -1, 9 and 19, for example, are all the
| same element of the modulo 10 congruence. 9 is called the
| "least positive residue", which is sometimes important.
| marcosdumay wrote:
| That's not any clearer. Both "modulo" and "remainder" can be
| defined in ways (yeah, more than one) that include or exclude
| negative numbers.
|
| You have to memorize the full definition. There's no mnemonic
| shortcut.
| seanhunter wrote:
| Neither of them is wrong. Languages (like python) which only
| return a positive number are returning the least positive
| residue of division under the modulus. That is the most
| common interpretation of it in number theory at least far as
| I know.
|
| C generally does return negative numbers when you use the %
| operator.
| DonHopkins wrote:
| Forth-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://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
|
| 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_di...
|
| Lots of other languages got it wrong:
|
| 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...
|
| >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 wrote:
| They 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 wrote:
| in 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-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
| 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 wrote:
| IEEE 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 wrote:
| there'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?
| jmalicki wrote:
| On Linux, fpenableexcept(3) lets you do this.
| kragen wrote:
| aha! feenableexcept! i'd never heard of it!
| // Demonstrate C99/glibc 2.2+ feenableexcept
| #define _GNU_SOURCE #include <math.h>
| #include <fenv.h> #include <stdlib.h>
| #include <stdio.h> int main(int argc, char
| **argv) { if (argc != 3) {
| fprintf(stderr, "Usage: %s a b # floating-point divide a
| by b\n", argv[0]); return 1; }
| feenableexcept(FE_DIVBYZERO); double a =
| atof(argv[1]), b = atof(argv[2]); printf("%g /
| %g = %g\n", a, b, a/b); return 0; }
|
| is the machine still ieee-754-compliant after you call
| feenableexcept? i'm guessing that if it weren't, intel
| wouldn't have defined the architecture to have this
| capability, given the historically close relationship
| between 8087 and ieee-754
| pclmulqdq wrote:
| See my other comment, but it's still compliant. You are
| allowed to handle exceptions in a lot of different ways.
| pclmulqdq wrote:
| I have been involved in the IEEE 754 committee on and
| off, so I will say that exceptions in the current
| standard are purposely sort of ambiguous in order to
| allow parallel and hardware implementations to also be
| standards-compliant (rather than just serial CPUs), and
| the exception-handling discussion has already been raised
| again for 754-2029. Exceptions as defined in the standard
| are the general form of error handling for IEEE 754.
|
| Signaling NaNs (sNaN) are the only form of "signaling"
| that I am aware of, and sNaNs are just set up to trigger
| an "invalid operation" exception. I believe the idea is
| that an operation raises an sNaN when it creates a NaN,
| and a subsequent operation can catch it and issue the
| exception so you can figure out exactly where the
| calculation went wrong. If you ignore that exception, the
| sNaN becomes a quiet NaN (qNaN) and propagates.
|
| The idea of traps have been written out of the standard
| in favor of exceptions. Traps had a defined behavior,
| which is what you would expect as an "exception" in most
| programming languages. A sibling comment indicated how to
| enable the FP exceptions as CPU interrupts, which become
| OS traps, and then become exceptions in Python/Java/C++
| (any language with exceptions). Totally not confusing
| terminology at all.
|
| I don't know off the top of my head of any architecture
| that treats all the exceptions in this way by default.
| One of the other exceptions is "inexact" which comes up
| on many calculations. I don't think you would want that
| to generally cause a CPU interrupt. Most implementations
| dump the exceptions as flags into a status register and
| let you see what happened if you care.
| kragen wrote:
| thank you, this helps a lot!
| hawski wrote:
| IEEE 754 has infinity, it has signed zeros, but it doesn't
| have the absolute zero. For the math to be entirely correct
| it is not enough. I wonder why it was done like this.
| kragen wrote:
| you're in luck! that's such a frequently asked question
| that kahan has written several papers about it, and
| there's a wikipedia page too:
| https://en.wikipedia.org/wiki/Signed_zero
| pclmulqdq wrote:
| +/-0 should generally be thought of as 0. It's just "0
| from the right" and "0 from the left." The reason it has
| +/-0 is for a few specific applications:
|
| - Some people really do want 1/(-x) to be negative
| infinity when a positive x reaches underflow. This helps
| with some kinds of numerical simulations since it can
| preserve the sign bit through underflow.
|
| - Interval arithmetic implementations need to be able to
| disambiguate closed and open intervals at 0.
|
| If you turn on fast math mode, you can also essentially
| disable the special treatment of the zeros.
| hawski wrote:
| But still, why there is no absolute 0 - the third zero? I
| would prefer something like this: 1 / +0
| == +inf 1 / -0 == -inf 1 / 0 == nan
| abs(-0) == 0
|
| But it is not possible as there is no unsigned/absolute
| zero: 0 means +0. I guess it makes things much simpler,
| but IMHO it makes it a bit less useful and strange.
| pclmulqdq wrote:
| I think if you treat infinity as a special case of NaN
| (which it basically is for many math systems), you get
| the behavior you want. A lot of people think that +/-0
| represent +/-epsilon, but they really don't. +0 is the
| default 0, and -0 is what you get when you underflow
| specifically from the left.
| kstrauser wrote:
| I am _soooo_ glad that Python throws errors there. Instead of
| being surprised when your math starts giving unexpected
| results, it blows up and makes you deal with the error. There
| 's no situation where I'd prefer silent incorrectness.
| rightbyte wrote:
| Inf is not incorrect.
|
| Division by the two almost zero numbers will also give
| "unexpected" results, if inf is unexpected.
| hawski wrote:
| But it is incorrect. Positive or negative number divided by
| zero is either +infinity or -infinity. For a positive
| dividend it would be +infinity only if the divisor got
| there from a positive number towards absolute 0, but at the
| time of doing the division we only have absolute 0 as the
| divisor.
|
| The usual floating point behavior of having signed zero (+0
| and -0) is not enough for this to be correct. You would
| also need a normal proper unsigned absolute 0 as well. Then
| you could give +inf for division with +0 and -inf for
| division with -0 and error or nan with division by the
| absolute (unsigned) 0. As far as I understand there is no
| unsigned 0 in IEEE 754, 0 is +0, so in reality it is always
| signed.
| kstrauser wrote:
| Furthermore, it's almost never the case that you
| legitimately expect to divide by zero. In the case of
| Python where exception handling is pervasive and
| idiomatic, it's far more reasonable to treat that as,
| well, _an exception_. If you 're doing an operation that
| sometimes execute divide-by-zero, you can watch out for
| it and handle it appropriately. When you're not expecting
| to be doing that calculation, it's an error.
| kstrauser wrote:
| You're right about the unexpected results in that case, but
| I'd bet good money that accidentally dividing by zero in
| code like `avg=sum(lst)/len(lst)` is far more common in
| standard Python code than dividing very small numbers.
| netcraft wrote:
| totally agreed, and its so common that some places like
| snowflake give you div0 and div0null to opt into just
| returning 0 or null in those cases
| cygx wrote:
| _They made the correct decision here!_
|
| No love for Euclidean division?
| aimor wrote:
| What 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 wrote:
| You convert to float, divide and then do floor, ceil or
| whatever.
| eugenekolo wrote:
| `int(a/b)` instead of `a//b` (for trunc to zero behavior.
| especially important when working with c or disassembled code)
| orlp wrote:
| Assuming 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 wrote:
| I 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 wrote:
| It's nice for general numerical methods but somewhat annoying
| for implementing integer-arithmetic algorithms.
| taeric wrote:
| Yeah, 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 wrote:
| To be fair, Python's `/` creates floats, and this article is
| about a second operator `//`.
| taeric wrote:
| Continuing 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 wrote:
| In python 2, '/' creates integers, and the article was
| written in 2010 when python 2 was the most used by far.
| lalaithion wrote:
| from the link:
|
| > PS. Note that I am using // instead of / -- this is
| Python 3 syntax
| im3w1l wrote:
| Oh. Right.
| shawn_w wrote:
| Scheme 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 wrote:
| Discussed at the time:
|
| _Why Python 's Integer Division Floors_ -
| https://news.ycombinator.com/item?id=1630394 - Aug 2010 (2
| comments)
___________________________________________________________________
(page generated 2024-02-28 23:02 UTC)