- Notifications
You must be signed in to change notification settings - Fork 294
Open
Labels
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.)