OLD | NEW |
(Empty) | |
| 1 """ |
| 2 ``python-future``: pure Python implementation of Python 3 round(). |
| 3 """ |
| 4 |
| 5 from future.utils import PYPY, PY26, bind_method |
| 6 |
| 7 # Use the decimal module for simplicity of implementation (and |
| 8 # hopefully correctness). |
| 9 from decimal import Decimal, ROUND_HALF_EVEN |
| 10 |
| 11 |
| 12 def 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 not PY26: |
| 42 d = Decimal.from_float(number).quantize(exponent, |
| 43 rounding=ROUND_HALF_EVEN) |
| 44 else: |
| 45 d = from_float_26(number).quantize(exponent, rounding=ROUND_HALF_EVEN) |
| 46 |
| 47 if return_int: |
| 48 return int(d) |
| 49 else: |
| 50 return float(d) |
| 51 |
| 52 |
| 53 ### From Python 2.7's decimal.py. Only needed to support Py2.6: |
| 54 |
| 55 def from_float_26(f): |
| 56 """Converts a float to a decimal number, exactly. |
| 57 |
| 58 Note that Decimal.from_float(0.1) is not the same as Decimal('0.1'). |
| 59 Since 0.1 is not exactly representable in binary floating point, the |
| 60 value is stored as the nearest representable value which is |
| 61 0x1.999999999999ap-4. The exact equivalent of the value in decimal |
| 62 is 0.1000000000000000055511151231257827021181583404541015625. |
| 63 |
| 64 >>> Decimal.from_float(0.1) |
| 65 Decimal('0.1000000000000000055511151231257827021181583404541015625') |
| 66 >>> Decimal.from_float(float('nan')) |
| 67 Decimal('NaN') |
| 68 >>> Decimal.from_float(float('inf')) |
| 69 Decimal('Infinity') |
| 70 >>> Decimal.from_float(-float('inf')) |
| 71 Decimal('-Infinity') |
| 72 >>> Decimal.from_float(-0.0) |
| 73 Decimal('-0') |
| 74 |
| 75 """ |
| 76 import math as _math |
| 77 from decimal import _dec_from_triple # only available on Py2.6 and Py2.7
(not 3.3) |
| 78 |
| 79 if isinstance(f, (int, long)): # handle integer inputs |
| 80 return Decimal(f) |
| 81 if _math.isinf(f) or _math.isnan(f): # raises TypeError if not a float |
| 82 return Decimal(repr(f)) |
| 83 if _math.copysign(1.0, f) == 1.0: |
| 84 sign = 0 |
| 85 else: |
| 86 sign = 1 |
| 87 n, d = abs(f).as_integer_ratio() |
| 88 # int.bit_length() method doesn't exist on Py2.6: |
| 89 def bit_length(d): |
| 90 if d != 0: |
| 91 return len(bin(abs(d))) - 2 |
| 92 else: |
| 93 return 0 |
| 94 k = bit_length(d) - 1 |
| 95 result = _dec_from_triple(sign, str(n*5**k), -k) |
| 96 return result |
| 97 |
| 98 |
| 99 __all__ = ['newround'] |
OLD | NEW |