"""compatibility layer for decimal (Python standard library)""" from __future__ import absolute_import from decimal import * try: Decimal.from_float # New in 2.7 except AttributeError: import math as _math def _bit_length(integer): s = bin(integer) # binary representation: bin(-37) --> '-0b100101' s = s.lstrip('-0b') # remove leading zeros and minus sign return len(s) # len('100101') --> 6 @classmethod def _from_float(cls, f): if isinstance(f, int): # handle integer inputs return cls(f) if not isinstance(f, float): raise TypeError("argument must be int or float.") if _math.isinf(f) or _math.isnan(f): return cls(repr(f)) if _math.copysign(1.0, f) == 1.0: sign = 0 else: sign = 1 n, d = abs(f).as_integer_ratio() #k = d.bit_length() - 1 k = _bit_length(d) - 1 result = _dec_from_triple(sign, str(n*5**k), -k) if cls is Decimal: return result else: return cls(result) Decimal.from_float = _from_float if Decimal('1.0') != 1.0: # Changed in Python 3.2 import numbers as _numbers from decimal import _dec_from_triple class FloatOperation(DecimalException, TypeError): """Enable stricter semantics for mixing floats and Decimals.""" pass # Adapted from Python 3.1 standard library. _context_init_orig = Context.__init__ def _context_init_new(self, prec=None, rounding=None, traps=None, flags=None, Emin=None, Emax=None, capitals=None, _clamp=0, _ignored_flags=None): # Call original __init__. _context_init_orig(self, prec=prec, rounding=rounding, traps=traps, flags=flags, Emin=Emin, Emax=Emax, capitals=capitals, _clamp=_clamp, _ignored_flags=_ignored_flags) # Add FloatOperation to `traps` dict. self.traps[FloatOperation] = 0 Context.__init__ = _context_init_new # Adapted from Python 3.4 standard library. def _convert_for_comparison(self, other, equality_op=False): if isinstance(other, Decimal): return self, other if isinstance(other, _numbers.Rational): if not self._is_special: self = _dec_from_triple(self._sign, str(int(self._int) * other.denominator), self._exp) return self, Decimal(other.numerator) if equality_op and isinstance(other, _numbers.Complex) and other.imag == 0: other = other.real if isinstance(other, float): context = getcontext() if equality_op: context.flags[FloatOperation] = 1 else: context._raise_error(FloatOperation, "strict semantics for mixing floats and Decimals are enabled") return self, Decimal.from_float(other) return NotImplemented, NotImplemented def _eq(self, other, context=None): self, other = _convert_for_comparison(self, other, equality_op=True) if other is NotImplemented: return other if self._check_nans(other, context): return False return self._cmp(other) == 0 Decimal.__eq__ = _eq def _ne(self, other, context=None): self, other = _convert_for_comparison(self, other, equality_op=True) if other is NotImplemented: return other if self._check_nans(other, context): return True return self._cmp(other) != 0 Decimal.__ne__ = _ne def _lt(self, other, context=None): self, other = _convert_for_comparison(self, other) if other is NotImplemented: return other ans = self._compare_check_nans(other, context) if ans: return False return self._cmp(other) < 0 Decimal.__lt__ = _lt def _le(self, other, context=None): self, other = _convert_for_comparison(self, other) if other is NotImplemented: return other ans = self._compare_check_nans(other, context) if ans: return False return self._cmp(other) <= 0 Decimal.__le__ = _le def _gt(self, other, context=None): self, other = _convert_for_comparison(self, other) if other is NotImplemented: return other ans = self._compare_check_nans(other, context) if ans: return False return self._cmp(other) > 0 Decimal.__gt__ = _gt def _ge(self, other, context=None): self, other = _convert_for_comparison(self, other) if other is NotImplemented: return other ans = self._compare_check_nans(other, context) if ans: return False return self._cmp(other) >= 0 Decimal.__ge__ = _ge