Open
Description
[Prompted by this StackOverflow question]
There are a couple of issues with the current implementation of round
from builtins
:
- it doesn't support negative second argument:
>>> from builtins import round
>>> round(314, -2)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/future/builtins/newround.py", line 33, in newround
raise NotImplementedError('negative ndigits not supported yet')
NotImplementedError: negative ndigits not supported yet
- it can raise
decimal.InvalidOperation
:
>>> round(1e50, 2)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/future/builtins/newround.py", line 43, in newround
rounding=ROUND_HALF_EVEN)
File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/decimal.py", line 2468, in quantize
'quantize result has too many digits for current context')
File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/decimal.py", line 3872, in _raise_error
raise error(explanation)
decimal.InvalidOperation: quantize result has too many digits for current context
The first restriction seems unnecessary. The second can be overcome by using a dedicated decimal
context with sufficient precision. For rounding a float x
to n
decimal places (with n
possibly negative), I'd suggest something like the following:
from decimal import Context, Decimal
c = Context(prec=800) # sufficient precision that the `quantize` result is representable
quantum = Decimal("1e{}".format(-n)) # should work for both n positive and negative
rounded = float(c.quantize(Decimal(x), quantum))
(The bound of 800 here is a bit crude. The maximum number of significant digits needed to represent any IEEE 754 binary64 float exactly in decimal is 767.)