| Index: third_party/google-endpoints/future/builtins/newround.py
|
| diff --git a/third_party/google-endpoints/future/builtins/newround.py b/third_party/google-endpoints/future/builtins/newround.py
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..f59b35b325f74d06da0b45e5d3ff85b14a896c11
|
| --- /dev/null
|
| +++ b/third_party/google-endpoints/future/builtins/newround.py
|
| @@ -0,0 +1,99 @@
|
| +"""
|
| +``python-future``: pure Python implementation of Python 3 round().
|
| +"""
|
| +
|
| +from future.utils import PYPY, PY26, bind_method
|
| +
|
| +# Use the decimal module for simplicity of implementation (and
|
| +# hopefully correctness).
|
| +from decimal import Decimal, ROUND_HALF_EVEN
|
| +
|
| +
|
| +def newround(number, ndigits=None):
|
| + """
|
| + See Python 3 documentation: uses Banker's Rounding.
|
| +
|
| + Delegates to the __round__ method if for some reason this exists.
|
| +
|
| + If not, rounds a number to a given precision in decimal digits (default
|
| + 0 digits). This returns an int when called with one argument,
|
| + otherwise the same type as the number. ndigits may be negative.
|
| +
|
| + See the test_round method in future/tests/test_builtins.py for
|
| + examples.
|
| + """
|
| + return_int = False
|
| + if ndigits is None:
|
| + return_int = True
|
| + ndigits = 0
|
| + if hasattr(number, '__round__'):
|
| + return number.__round__(ndigits)
|
| +
|
| + if ndigits < 0:
|
| + raise NotImplementedError('negative ndigits not supported yet')
|
| + exponent = Decimal('10') ** (-ndigits)
|
| +
|
| + if PYPY:
|
| + # Work around issue #24: round() breaks on PyPy with NumPy's types
|
| + if 'numpy' in repr(type(number)):
|
| + number = float(number)
|
| +
|
| + if not PY26:
|
| + d = Decimal.from_float(number).quantize(exponent,
|
| + rounding=ROUND_HALF_EVEN)
|
| + else:
|
| + d = from_float_26(number).quantize(exponent, rounding=ROUND_HALF_EVEN)
|
| +
|
| + if return_int:
|
| + return int(d)
|
| + else:
|
| + return float(d)
|
| +
|
| +
|
| +### From Python 2.7's decimal.py. Only needed to support Py2.6:
|
| +
|
| +def from_float_26(f):
|
| + """Converts a float to a decimal number, exactly.
|
| +
|
| + Note that Decimal.from_float(0.1) is not the same as Decimal('0.1').
|
| + Since 0.1 is not exactly representable in binary floating point, the
|
| + value is stored as the nearest representable value which is
|
| + 0x1.999999999999ap-4. The exact equivalent of the value in decimal
|
| + is 0.1000000000000000055511151231257827021181583404541015625.
|
| +
|
| + >>> Decimal.from_float(0.1)
|
| + Decimal('0.1000000000000000055511151231257827021181583404541015625')
|
| + >>> Decimal.from_float(float('nan'))
|
| + Decimal('NaN')
|
| + >>> Decimal.from_float(float('inf'))
|
| + Decimal('Infinity')
|
| + >>> Decimal.from_float(-float('inf'))
|
| + Decimal('-Infinity')
|
| + >>> Decimal.from_float(-0.0)
|
| + Decimal('-0')
|
| +
|
| + """
|
| + import math as _math
|
| + from decimal import _dec_from_triple # only available on Py2.6 and Py2.7 (not 3.3)
|
| +
|
| + if isinstance(f, (int, long)): # handle integer inputs
|
| + return Decimal(f)
|
| + if _math.isinf(f) or _math.isnan(f): # raises TypeError if not a float
|
| + return Decimal(repr(f))
|
| + if _math.copysign(1.0, f) == 1.0:
|
| + sign = 0
|
| + else:
|
| + sign = 1
|
| + n, d = abs(f).as_integer_ratio()
|
| + # int.bit_length() method doesn't exist on Py2.6:
|
| + def bit_length(d):
|
| + if d != 0:
|
| + return len(bin(abs(d))) - 2
|
| + else:
|
| + return 0
|
| + k = bit_length(d) - 1
|
| + result = _dec_from_triple(sign, str(n*5**k), -k)
|
| + return result
|
| +
|
| +
|
| +__all__ = ['newround']
|
|
|