round issue

Mark Dickinson dickinsm at gmail.com
Mon Jul 12 06:44:23 EDT 2010


On Jul 12, 10:52 am, Robin Becker <ro... at reportlab.com> wrote:
> What value should round(-9.85,1) return? Is the result explainable in python (ie
> without resort to the internal FP representations etc etc)?

As you observe, the closest float to -9.85 is actually just a little
smaller (i.e., closer to 0) than -9.85:

Python 2.7 (r27:82500, Jul 11 2010, 22:38:53)
[GCC 4.2.1 (Apple Inc. build 5659)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import decimal
>>> decimal.Decimal(-9.85)
Decimal('-9.8499999999999996447286321199499070644378662109375')

So you're right: round(-9.85, 1) *should* return -9.8.  The 2.x (for x
<= 6) version of round is a bit deficient in that respect.
Internally, it's doing the obvious thing:  namely, multiplying by
10.0, rounding to the nearest integer, then dividing by 10.0.  The
problem is that this not-quite-9.85 value, when multiplied by 10,
becomes (as a result of rounding error) *exactly* 98.5, which then
gets rounded *up* to 99 instead of down to 98.

This is fixed in Python 2.7, and in Python 3.x.  (The code that was
introduced for the new short float repr made it easy to fix.)

That said, if your client really *means* -9.85 (rather than some
binary approximation to it), and wants it to round in a predictable
manner, the right way to fix this would be to use Decimal instead of
float to represent the number.  That way, you can also specify what
rounding mode you want (instead of relying on the default round-half-
away-from-zero in 2.x or round-half-to-even in 3.x.)

>>> decimal.Decimal('-9.85').quantize(decimal.Decimal('0.1'), rounding=decimal.ROUND_HALF_UP)
Decimal('-9.9')
>>> decimal.Decimal('-9.85').quantize(decimal.Decimal('0.1'), rounding=decimal.ROUND_HALF_EVEN)
Decimal('-9.8')

--
Mark



More information about the Python-list mailing list