[Tutor] / vs //
Tim Peters
tim.one@comcast.net
Wed, 20 Feb 2002 18:13:47 -0500
[Christopher Smith]
> The two operations below yield different results...does / know something
> that // doesn't know?
It's actually the reverse: // knows something / doesn't! This isn't easy
to understand, though, and it will take real effort if you want to
understand it fully. I do, but I suggest nobody else bother <wink>.
> >>> 3.9//1.3
> 2.0
> >>> 3.9/1.3
> 3.0
>
> It seems like // is being technically corrrect since 3.9 is a little
> smaller than 3.9
It's also the case that 1.3 is an approximation, and that the number stored
in the machine is a little larger than (the mathematical) 13/10. If 13/10 =
I/2**J exactly for some integers I and J, then 13*2**J = 10*I exactly. But
5 divides 10*I evenly, therefore must also divide 13*2**J evenly. But there
is no integer J such that 5 divides 13*2**J evenly. Therefore the decimal
number 1.3 cannot be represented exactly as a binary floating-point number
with any finite number of bits.
> but that / is being a little smarter and trying to give you a closer
> approximation.
Nope. "/" takes the two approximations, divides them "as if" with infinite
precison, then *rounds* the result to 53 significant bits. floor() doesn't
round; for positive inputs, floor truncates instead.
> >>> 3.9
> 3.8999999999999999
> >>> 1.3
> 1.3
As above, 1.3 isn't exactly 13/10 either, but it is so *close* to 13/10 that
rounding its true decimal value to 17 significant decimal digits yields
"1.3". Precisely, assuming your platform C library does best-possible
conversion of decimal strings to IEEE doubles, the value stored for 1.3 is
exactly
11709359031163290
-----------------
9007199254740992 (this is 2**54, by the way)
and converting that back to decimal again gives
1.300000000000000044408920985006...
If you round that to 17 significant decimal digits, you get the "1.3" that
Python displays.
> PEP 238 says that a//b should act like floor(a/b) but you can see in this
> example that it doesn't since that result is 3.0 not 2.0
Another subtletly: the docs say "floor", not "math.floor". floor is a
mathematical function; math.floor is a computer implementation of that
mathematical function, but subject to limitations due to finite
floating-point accuracy. In much the same way, floating-point numbers
themselves are an approximation to real numbers.
And another subtlety: the "/" in the doc's "floor(a/b)" means mathematical
("infinite precision") division, not computer floating-point division.
> >>> math.floor(3.9/1.3)
> 3.0
The approximation computed for 3.9/1.3 happens to be exactly 3.0, so
math.floor() leaves it alone.
// is the smarter one here. It knows that 3.9/1.3 does not have an exact
result, and indeed that if carried out to infinite precision it would give a
quotient of 2 and leave a remainder of
>>> math.fmod(3.9, 1.3)
1.2999999999999998
>>>
What "//" returns is the first element of the tuple returned by the builtin
divmod():
>>> divmod(3.9, 1.3)
(2.0, 1.2999999999999998)
>>>
The important point to take from all this is that floor truncates positive
inputs, while floating-point "/" rounds.
I told you at the start it would be painful <wink>.