# [Tutor] floating point rounding inconsistency

Steven D'Aprano steve at pearwood.info
Sat Sep 29 05:02:56 CEST 2012

On 29/09/12 11:30, Alan Gauld wrote:
> On 28/09/12 13:15, Mark Lawrence wrote:
>
>> from Calvin Spealman is typical "Also, I'd be completely in support of
>> dropping round() and agree it gets misused
>> and leads to too much confusion. We should promote the right ways, and
>> sometimes to show the right path you need to lock another door and throw
>> away the key.".
>
> As a matter of interest what is the "right path" that is being proposed?
> If it takes much more than 7 keypresses then I suspect it will be opposed!
>(I'm too lazy to look up the thread myself! :-)

It is already opposed because it breaks existing, working code unnecessarily.

The replacements suggested are:

- use Decimal values, and round them instead;

- use string formatting

Neither suggestion has really thought things through clearly. The first has
identified the problem correctly -- it is *binary floats*, not round, which
causes the problem, but to round a Decimal you need the round built-in (or
at least a replacement):

py> from decimal import Decimal as D
py> x = D('2.123456')
py> x.round
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Decimal' object has no attribute 'round'
py> round(x, 3)
Decimal('2.123')

The second is, well, poorly thought out. Here is an example of the sort
of thing where round can give funny results. You expect that on half-way
cases, it should round to the nearest EVEN number:

# as expected
py> round(1.125, 2)
1.12
py> round(1.135, 2)
1.14

# but unexpected
py> round(2.675, 2)
2.67

Why? Because 2.675 is not actually a half-way case, it is actually a binary
float a tiny bit under the decimal 2.675:

py> print("%.17f" % 2.675)
2.67499999999999982

Fair enough. So let's try the recommended solution:

py> "%.2f" % 2.675
'2.67'

Wait, that gives the same result as rounding. So how is this better?

--
Steven