[Python-Dev] Floor division
Tim Peters
tim.peters at gmail.com
Mon Jan 22 03:08:18 CET 2007
[Tim Peters]
>> ...
>> >>> decimal.Decimal(-1) % decimal.Decimal("1e100")
>> Decimal("-1")
[Armin Rigo]
> BTW - isn't that case in contradiction with the general Python rule that
> if b > 0, then a % b should return a number between 0 included and b
> excluded?
Sure.
> We try hard to do that for ints, longs and floats.
But fail in this case for floats:
>>> -1 % 1e100 < 1e100
False
Can't win. The infinite-precision result is the mathematical
F(1e100)-1, where F(1e100) is the binary float closest to 10**100, but
F(1e100)-1 isn't representable as a float -- it rounds back up to
F(1e100):
>>> -1 % 1e100 == 1e100
True
There simply is no /representable/ float value in [0, 10**100)
congruent to -1 modulo 10**100 (or modulo F(1e100)), so it's
impossible to return a non-surprising (to everyone) result in that
range. 0 and 1e100 are in some sense "the best" answers in that
range, both off by only 1 part in F(1e100), the smallest possible
error among representable floats in that range. -1/1e100 certainly
isn't 0, so
-1 // 1e100 == -1.0
is required. Picking -1 % 1e100 == 1e100 then follows, to try to preserve that
a = (a//b)*b + a%b
as closely as is possible.
Ints and longs never have problems here, because the exact % result is
always exactly representable.
That isn't true of floats (whether binary or decimal), but under a
different definition of "mod" the mathematically exact result is
always exactly representable: a%b takes the sign of `a` rather than
the sign of `b`. C's fmod (Python's math.fmod), and the proposed
standard for decimal arithmetic implemented by the `decimal` module,
use that meaning for "mod" instead.
>>> math.fmod(-1, 1e100)
-1.0
> The fact that it works differently with Decimal could be unexpected.
Yup. See "can't win" above :-(
Another good definition of "mod" for floats is to return the
representative of smallest absolute value; i.e., satisfy
abs(a%b) <= abs(b) / 2
The mathematically exact value for that is also exactly representable
(BTW, the proposed standard for decimal arithmetic calls this
"remainder-near", as opposed to "remainder").
It's just a fact that different definitions of mod are most useful
most often depending on data type. Python's is good for integers and
often sucks for floats. The C99 and `decimal` definition(s) is/are
good for floats and often suck(s) for integers. Trying to pretend
that integers are a subset of floats can't always work ;-)
More information about the Python-Dev
mailing list