A use for integer quotients

Guido van Rossum guido at python.org
Mon Jul 23 07:46:45 EDT 2001


> [David Eppstein]
> > I don't suppose it would be possible to go through some repository of
> > Python sources and figure out the relative numbers of divisions
> > of integer arguments that end up an int versus the ones that are
> > coerced to floats?

[Tim Peters]
> Guido did this for the Python library, and found three instances of "/" that
> would break.

Alas, all this suggests is that the test suite *still* doesn't have
enough coverage.  Looking for '/' tokens I found 30 modules using '/'
amongst Lib/*.py, and a quick inspection suggests that the majority of
these use integer division -- an expected result, given the
over-emphasis of the standard library on classic data types and
algorithms, which were mostly developed in a culture that despises
floating point numbers as too expensive. :-)

But I also found a likely bug!  In colorsys.py, there are two routines
(rgb_to_hls() and rgb_to_hsv()) that seem to be doing floating point
math using (r, g, b) inputs and don't take any consistent precaution
to cast these inputs to floats before dividing them.  The outputs are
definitely intended to be floats.  The inputs are likely RGB triples
taken from images, which are usually ints.

This is why making / fractional division and // truncated division
will reduce the number of bugs in new code: for all occurrences of
truncated division, the author *knows* she wants truncated division,
and it's easy to remember to use // because using / gives an
immediately wrong result.  When the users wants fractional division
though, it's common to test only with float inputs, or to miss a path
through the code where ints aren't casts to floats before they are
being divided.  Then, much later, when an int gets passed and a
slightly wrong result is calculated, it takes hours of debugging to
discover the error.

Note that this kind of bug is unique to Python (or dynamically typed
languages in general): in C, the arguments would have been declared as
floats and the problem would have been prevented.

Reiterating why the current definition of '/' is evil: int and float
together aren't really two distinct types: they are at most a
type-and-a-half, and the integers are embedded in the space of floats
for most practical purposes.  Division is the one exception, and it
hurts.  Nobody wants to write polymorphic code where '/' means
truncated division for int arguments but fractional division for float
arguments.  You know which kind of division you want when you write
the code, but there's no way to spell it.

(I've seen this countered with the argument that classes can overload
/ any way they want, and thus you can't rely on the "meaning" of /
anyway.  But there you get what you ask for, and it's up to the author
of the class to decide how her class needs to behave in the context of
other numeric types.  How integers behave in the context of floats is
already decided: you should be able to pass an int whenever something
requires a float.)

Of course, I'm well aware of the issues around maintaining old code.
We will have to carry around "from __future__ import division" for a
loooooong time.  Python 2.2 is the first opportunity to introduce it,
so let's not be shy.

--Guido van Rossum (home page: http://www.python.org/~guido/)



More information about the Python-list mailing list