Python 3 Decimal rounding half down with ROUND_HALF_UP context
decimal, python, python-3.x, rounding
Solution
Notice that when you call `round` you are getting a float value as a result, not a `Decimal`. `round` is coercing the value to a float and then rounding that according to the rules for rounding a float.
If you use the optional `ndigits` parameter when you call `round()` you will get back a Decimal result and in this case it will round the way you expected.
Python 3.4.1 (default, Sep 24 2015, 20:41:10)
[GCC 4.9.2 20150212 (Red Hat 4.9.2-6)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import decimal
>>> context = decimal.getcontext()
>>> context.rounding = decimal.ROUND_HALF_UP
>>> round(decimal.Decimal('2.5'), 0)
Decimal('3')
I haven't found where it is documented that `round(someDecimal)` returns an int but `round(someDecimal, ndigits)` returns a decimal, but that seems to be what happens in Python 3.3 and later. In Python 2.7 you always get a float back when you call `round()` but Python 3.3 improved the integration of `Decimal` with the Python builtins.
As noted in a comment, `round()` delegates to `Decimal.__round__()` and that indeed shows the same behaviour:
>>> Decimal('2.5').__round__()
2
>>> Decimal('2.5').__round__(0)
Decimal('3')
I note that the documentation for `Fraction` says:
__round__()
__round__(ndigits)
The first version returns the nearest int to self, rounding half to even.
The second version rounds self to the nearest multiple of Fraction(1, 10**ndigits)
(logically, if ndigits is negative), again rounding half toward even.
This method can also be accessed through the round() function.
Thus the behaviour is consistent in that with no argument it changes the type of the result and rounds half to even, however it seems that `Decimal` fails to document the behaviour of its `__round__` method.
Edit to note as Barry Hurley says in the comments, `round()` is documented as returning a `int` if called without the optional arguments and a "floating point value" if given the optional argument. https://docs.python.org/3/library/functions.html#round
Problem
Can anybody explain or propose a fix for why when I round a decimal in Python 3 with the context set to round half up, it rounds 2.5 to 2, whereas in Python 2 it rounds correctly to 3: Python 3.4.3 and 3.5.2: ``` >>> import decimal >>> context = decimal.getcontext() >>> context.rounding = decimal.ROUND_HALF_UP >>> round(decimal.Decimal('2.5')) 2 >>> decimal.Decimal('2.5').__round__() 2 >>> decimal.Decimal('2.5').quantize(decimal.Decimal('1'), rounding=decimal.ROUND_HALF_UP) Decimal('3') ``` Python 2.7.6: ``` >>> import decimal >>> context = decimal.getcontext() >>> context.rounding = decimal.ROUND_HALF_UP >>> round(decimal.Decimal('2.5')) 3.0 >>> decimal.Decimal('2.5').quantize(decimal.Decimal('1'), rounding=decimal.ROUND_HALF_UP) Decimal('3') ```