[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>.