1""" 2``python-future``: pure Python implementation of Python 3 round(). 3""" 4 5from future.utils import PYPY, PY26, bind_method 6 7# Use the decimal module for simplicity of implementation (and 8# hopefully correctness). 9from decimal import Decimal, ROUND_HALF_EVEN 10 11 12def newround(number, ndigits=None): 13 """ 14 See Python 3 documentation: uses Banker's Rounding. 15 16 Delegates to the __round__ method if for some reason this exists. 17 18 If not, rounds a number to a given precision in decimal digits (default 19 0 digits). This returns an int when called with one argument, 20 otherwise the same type as the number. ndigits may be negative. 21 22 See the test_round method in future/tests/test_builtins.py for 23 examples. 24 """ 25 return_int = False 26 if ndigits is None: 27 return_int = True 28 ndigits = 0 29 if hasattr(number, '__round__'): 30 return number.__round__(ndigits) 31 32 if ndigits < 0: 33 raise NotImplementedError('negative ndigits not supported yet') 34 exponent = Decimal('10') ** (-ndigits) 35 36 if PYPY: 37 # Work around issue #24: round() breaks on PyPy with NumPy's types 38 if 'numpy' in repr(type(number)): 39 number = float(number) 40 41 if isinstance(number, Decimal): 42 d = number 43 else: 44 if not PY26: 45 d = Decimal.from_float(number).quantize(exponent, 46 rounding=ROUND_HALF_EVEN) 47 else: 48 d = from_float_26(number).quantize(exponent, rounding=ROUND_HALF_EVEN) 49 50 if return_int: 51 return int(d) 52 else: 53 return float(d) 54 55 56### From Python 2.7's decimal.py. Only needed to support Py2.6: 57 58def from_float_26(f): 59 """Converts a float to a decimal number, exactly. 60 61 Note that Decimal.from_float(0.1) is not the same as Decimal('0.1'). 62 Since 0.1 is not exactly representable in binary floating point, the 63 value is stored as the nearest representable value which is 64 0x1.999999999999ap-4. The exact equivalent of the value in decimal 65 is 0.1000000000000000055511151231257827021181583404541015625. 66 67 >>> Decimal.from_float(0.1) 68 Decimal('0.1000000000000000055511151231257827021181583404541015625') 69 >>> Decimal.from_float(float('nan')) 70 Decimal('NaN') 71 >>> Decimal.from_float(float('inf')) 72 Decimal('Infinity') 73 >>> Decimal.from_float(-float('inf')) 74 Decimal('-Infinity') 75 >>> Decimal.from_float(-0.0) 76 Decimal('-0') 77 78 """ 79 import math as _math 80 from decimal import _dec_from_triple # only available on Py2.6 and Py2.7 (not 3.3) 81 82 if isinstance(f, (int, long)): # handle integer inputs 83 return Decimal(f) 84 if _math.isinf(f) or _math.isnan(f): # raises TypeError if not a float 85 return Decimal(repr(f)) 86 if _math.copysign(1.0, f) == 1.0: 87 sign = 0 88 else: 89 sign = 1 90 n, d = abs(f).as_integer_ratio() 91 # int.bit_length() method doesn't exist on Py2.6: 92 def bit_length(d): 93 if d != 0: 94 return len(bin(abs(d))) - 2 95 else: 96 return 0 97 k = bit_length(d) - 1 98 result = _dec_from_triple(sign, str(n*5**k), -k) 99 return result 100 101 102__all__ = ['newround'] 103