[Python-Dev] Change in Python 3's "round" behavior

Steven D'Aprano steve at pearwood.info
Sun Sep 30 19:35:30 EDT 2018


On Mon, Oct 01, 2018 at 10:50:36AM +1300, Greg Ewing wrote:
> Alex Walters wrote:
> >Other use case is finance, where you can end up with interest calculations
> >that are fractional of the base unit of currency.  US$2.345 is impossible 
> >to
> >represent in real currency, so it has to be rounded.
> 
> This brings us back to my original point about floating point
> accuracy. If you do your interest calculation in floating
> point binary, first it's very unlikely that it will come
> out ending in exactly 0.5 of a cent, 

And yet people (Alex, and he says others) are complaining about this 
change in behaviour. If getting exactly 0.5 is as unlikely as you claim, 
how would they notice?


> and secondly if you
> care about the details that much, you should be calculating
> in decimal, and being explicit about exactly what kind of
> rounding you're doing.

Why should people using float have a biased round just because "they 
should be using Decimal"? The choice to use Decimal is not up to us and 
there's nothing wrong with using float for many purposes. Those who do 
shouldn't be burdened with a biased round.

Regardless of whether it meets with the approval of the mathematically 
naive who think that primary school rounding is the "intuitive" (or 
only) way to round, the change was made something like a decade ago. It 
matches the behaviour of Julia, .Net, VBScript and I expect other 
languages and makes for a technically better default rounding mode.

With no overwhelmingly strong case for reverting to a biased rounding 
mode, I think this discussion is dead. If people want to discuss 
something more productive, we could talk about adding an optional 
argument to round() to take a rounding mode, or adding an equivalent to 
the math library.

I'll start off...

How about we move the rounding mode constants out of the decimal module 
and into the math module? That makes them more easily discoverable and 
importable (the math module is lightweight, the decimal module is not).

The decimal module would then import the constants from math (it already 
imports math so that's no extra dependency).

Then we can add a keyword only argument to round:

    round(number, ndigits=0, *, mode=ROUND_HALF_EVEN)

To use it, you can import the rounding mode you want from math:

    from math import ROUND_CEILING
    round(x, 3, mode=ROUND_CEILING)

and everyone is happy (he says optimistically).

It's a bit funny to have constants in the math module not actually used 
there, for the benefit of a builtin and Decimal, but I prefer that to 
either importing them from decimal or making them builtins.

Thoughts?



-- 
Steve


More information about the Python-Dev mailing list