[Python-Dev] Floor division (original) (raw)
Tim Peters tim.peters at gmail.com
Sat Jan 20 02:33:23 CET 2007
- Previous message: [Python-Dev] Floor division
- Next message: [Python-Dev] Floor division
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
[Raymond Hettinger]
I bumped into an oddity today:
6.0 // 0.001 != math.floor(6.0 / 0.001) In looking at Objects/floatobject.c, I was surprised to find that floatfloordivision() is implemented in terms of floatdivmod(). Does anyone know why it takes such a circuitous path? I had expected something simpler and faster: return PyFloatFromDouble(floor(a/b));
To preserve, so far as is possible with floats, that
(*) a == (a//b)*b + a%b
In this case the binary double closest to decimal 0.001 is
0.001000000000000000020816681711721685132943093776702880859375
which is a little bit larger than 1/1000. Therefore the mathematical value of a/b is a little bit smaller than 6/(1/1000) = 6000, and the true floor of the mathematically correct result is 5999.
a % b is always computed exactly (mathematical result == machine
result) under Python's definition whenever a and b have the same sign
(under C99's and the decimal
module's definition it's always exact
regardless of signs), and getting the exact value for a%b implies the
exact value for a//b must also be returned (when possible) in order to
preserve identity (*) above.
IOW, since
6.0 % 0.001 0.00099999999999987512
it would be inconsistent to return 6000 for 6.0 // 0.001:
6.0 - 6000 * 0.001 # this isn't close to the value of a%b 0.0 6.0 - 5999 * 0.001 # but this is close 0.00099999999999944578
Note that two rounding errors occur in computing a - N*b via binary doubles. If there were no rounding errors, we'd have
6 % b == 6.0 - 5999 * b
exactly where
b = 0.001000000000000000020816681711721685132943093776702880859375
is the value actually stored in the machine for 0.001:
import decimal decimal.getcontext().prec = 1000 # way more than needed import math # Set b to exact decimal value of binary double closest to 0.001. m, e = math.frexp(0.001) b = decimal.Decimal(int(m*2**53)) / decimal.Decimal(1 << (53-e)) # Then 6%b is exactly equal to 6 - 5999*b 6 % b == 6 - 5999*b True # Confirm that all decimal calculations were exact. decimal.getcontext().flags[decimal.Inexact] False # Confirm that floor(6/b) is 5999. int(6/b) 5999 print 6/b 5999.99999999999987509990972966989180234686226...
All that said, (*) doesn't make a lot of sense for floats to begin with (for that matter, Python's definition of mod alone doesn't make much sense for floats when the signs differ -- e.g.,
-1 % 1e100 1e+100 decimal.Decimal(-1) % decimal.Decimal("1e100") Decimal("-1")
).
- Previous message: [Python-Dev] Floor division
- Next message: [Python-Dev] Floor division
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]