summaryrefslogtreecommitdiff
path: root/Lib/decimal.py
diff options
context:
space:
mode:
Diffstat (limited to 'Lib/decimal.py')
-rw-r--r--Lib/decimal.py959
1 files changed, 771 insertions, 188 deletions
diff --git a/Lib/decimal.py b/Lib/decimal.py
index b3bdc7f0c5..3cb3b80efd 100644
--- a/Lib/decimal.py
+++ b/Lib/decimal.py
@@ -21,7 +21,7 @@
This is a Py2.3 implementation of decimal floating point arithmetic based on
the General Decimal Arithmetic Specification:
- www2.hursley.ibm.com/decimal/decarith.html
+ http://speleotrove.com/decimal/decarith.html
and IEEE standard 854-1987:
@@ -134,7 +134,10 @@ __all__ = [
'setcontext', 'getcontext', 'localcontext'
]
+__version__ = '1.70' # Highest version of the spec this complies with
+
import copy as _copy
+import math as _math
import numbers as _numbers
try:
@@ -645,11 +648,55 @@ class Decimal(object):
return self
if isinstance(value, float):
- raise TypeError("Cannot convert float to Decimal. " +
- "First convert the float to a string")
+ value = Decimal.from_float(value)
+ self._exp = value._exp
+ self._sign = value._sign
+ self._int = value._int
+ self._is_special = value._is_special
+ return self
raise TypeError("Cannot convert %r to Decimal" % value)
+ # @classmethod, but @decorator is not valid Python 2.3 syntax, so
+ # don't use it (see notes on Py2.3 compatibility at top of file)
+ def from_float(cls, 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')
+
+ """
+ if isinstance(f, (int, long)): # handle integer inputs
+ return cls(f)
+ if _math.isinf(f) or _math.isnan(f): # raises TypeError if not a float
+ 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
+ result = _dec_from_triple(sign, str(n*5**k), -k)
+ if cls is Decimal:
+ return result
+ else:
+ return cls(result)
+ from_float = classmethod(from_float)
+
def _isnan(self):
"""Returns whether the number is not actually one.
@@ -802,8 +849,11 @@ class Decimal(object):
# subject of what should happen for a comparison involving a NaN.
# We take the following approach:
#
- # == comparisons involving a NaN always return False
- # != comparisons involving a NaN always return True
+ # == comparisons involving a quiet NaN always return False
+ # != comparisons involving a quiet NaN always return True
+ # == or != comparisons involving a signaling NaN signal
+ # InvalidOperation, and return False or True as above if the
+ # InvalidOperation is not trapped.
# <, >, <= and >= comparisons involving a (quiet or signaling)
# NaN signal InvalidOperation, and return False if the
# InvalidOperation is not trapped.
@@ -811,24 +861,24 @@ class Decimal(object):
# This behavior is designed to conform as closely as possible to
# that specified by IEEE 754.
- def __eq__(self, other):
- other = _convert_other(other)
+ def __eq__(self, other, context=None):
+ other = _convert_other(other, allow_float=True)
if other is NotImplemented:
return other
- if self.is_nan() or other.is_nan():
+ if self._check_nans(other, context):
return False
return self._cmp(other) == 0
- def __ne__(self, other):
- other = _convert_other(other)
+ def __ne__(self, other, context=None):
+ other = _convert_other(other, allow_float=True)
if other is NotImplemented:
return other
- if self.is_nan() or other.is_nan():
+ if self._check_nans(other, context):
return True
return self._cmp(other) != 0
def __lt__(self, other, context=None):
- other = _convert_other(other)
+ other = _convert_other(other, allow_float=True)
if other is NotImplemented:
return other
ans = self._compare_check_nans(other, context)
@@ -837,7 +887,7 @@ class Decimal(object):
return self._cmp(other) < 0
def __le__(self, other, context=None):
- other = _convert_other(other)
+ other = _convert_other(other, allow_float=True)
if other is NotImplemented:
return other
ans = self._compare_check_nans(other, context)
@@ -846,7 +896,7 @@ class Decimal(object):
return self._cmp(other) <= 0
def __gt__(self, other, context=None):
- other = _convert_other(other)
+ other = _convert_other(other, allow_float=True)
if other is NotImplemented:
return other
ans = self._compare_check_nans(other, context)
@@ -855,7 +905,7 @@ class Decimal(object):
return self._cmp(other) > 0
def __ge__(self, other, context=None):
- other = _convert_other(other)
+ other = _convert_other(other, allow_float=True)
if other is NotImplemented:
return other
ans = self._compare_check_nans(other, context)
@@ -889,12 +939,34 @@ class Decimal(object):
# The hash of a nonspecial noninteger Decimal must depend only
# on the value of that Decimal, and not on its representation.
# For example: hash(Decimal('100E-1')) == hash(Decimal('10')).
+
+ # Equality comparisons involving signaling nans can raise an
+ # exception; since equality checks are implicitly and
+ # unpredictably used when checking set and dict membership, we
+ # prevent signaling nans from being used as set elements or
+ # dict keys by making __hash__ raise an exception.
if self._is_special:
- if self._isnan():
- raise TypeError('Cannot hash a NaN value.')
- return hash(str(self))
- if not self:
- return 0
+ if self.is_snan():
+ raise TypeError('Cannot hash a signaling NaN value.')
+ elif self.is_nan():
+ # 0 to match hash(float('nan'))
+ return 0
+ else:
+ # values chosen to match hash(float('inf')) and
+ # hash(float('-inf')).
+ if self._sign:
+ return -271828
+ else:
+ return 314159
+
+ # In Python 2.7, we're allowing comparisons (but not
+ # arithmetic operations) between floats and Decimals; so if
+ # a Decimal instance is exactly representable as a float then
+ # its hash should match that of the float.
+ self_as_float = float(self)
+ if Decimal.from_float(self_as_float) == self:
+ return hash(self_as_float)
+
if self._isinteger():
op = _WorkRep(self.to_integral_value())
# to make computation feasible for Decimals with large
@@ -996,14 +1068,16 @@ class Decimal(object):
if ans:
return ans
- if not self:
- # -Decimal('0') is Decimal('0'), not Decimal('-0')
+ if context is None:
+ context = getcontext()
+
+ if not self and context.rounding != ROUND_FLOOR:
+ # -Decimal('0') is Decimal('0'), not Decimal('-0'), except
+ # in ROUND_FLOOR rounding mode.
ans = self.copy_abs()
else:
ans = self.copy_negate()
- if context is None:
- context = getcontext()
return ans._fix(context)
def __pos__(self, context=None):
@@ -1016,14 +1090,15 @@ class Decimal(object):
if ans:
return ans
- if not self:
- # + (-0) = 0
+ if context is None:
+ context = getcontext()
+
+ if not self and context.rounding != ROUND_FLOOR:
+ # + (-0) = 0, except in ROUND_FLOOR rounding mode.
ans = self.copy_abs()
else:
ans = Decimal(self)
- if context is None:
- context = getcontext()
return ans._fix(context)
def __abs__(self, round=True, context=None):
@@ -1608,7 +1683,7 @@ class Decimal(object):
self = _dec_from_triple(self._sign, '1', exp_min-1)
digits = 0
rounding_method = self._pick_rounding_function[context.rounding]
- changed = getattr(self, rounding_method)(digits)
+ changed = rounding_method(self, digits)
coeff = self._int[:digits] or '0'
if changed > 0:
coeff = str(int(coeff)+1)
@@ -1648,8 +1723,6 @@ class Decimal(object):
# here self was representable to begin with; return unchanged
return Decimal(self)
- _pick_rounding_function = {}
-
# for each of the rounding functions below:
# self is a finite, nonzero Decimal
# prec is an integer satisfying 0 <= prec < len(self._int)
@@ -1716,6 +1789,17 @@ class Decimal(object):
else:
return -self._round_down(prec)
+ _pick_rounding_function = dict(
+ ROUND_DOWN = _round_down,
+ ROUND_UP = _round_up,
+ ROUND_HALF_UP = _round_half_up,
+ ROUND_HALF_DOWN = _round_half_down,
+ ROUND_HALF_EVEN = _round_half_even,
+ ROUND_CEILING = _round_ceiling,
+ ROUND_FLOOR = _round_floor,
+ ROUND_05UP = _round_05up,
+ )
+
def fma(self, other, third, context=None):
"""Fused multiply-add.
@@ -1858,9 +1942,9 @@ class Decimal(object):
nonzero. For efficiency, other._exp should not be too large,
so that 10**abs(other._exp) is a feasible calculation."""
- # In the comments below, we write x for the value of self and
- # y for the value of other. Write x = xc*10**xe and y =
- # yc*10**ye.
+ # In the comments below, we write x for the value of self and y for the
+ # value of other. Write x = xc*10**xe and abs(y) = yc*10**ye, with xc
+ # and yc positive integers not divisible by 10.
# The main purpose of this method is to identify the *failure*
# of x**y to be exactly representable with as little effort as
@@ -1868,13 +1952,12 @@ class Decimal(object):
# eliminate the possibility of x**y being exact. Only if all
# these tests are passed do we go on to actually compute x**y.
- # Here's the main idea. First normalize both x and y. We
- # express y as a rational m/n, with m and n relatively prime
- # and n>0. Then for x**y to be exactly representable (at
- # *any* precision), xc must be the nth power of a positive
- # integer and xe must be divisible by n. If m is negative
- # then additionally xc must be a power of either 2 or 5, hence
- # a power of 2**n or 5**n.
+ # Here's the main idea. Express y as a rational number m/n, with m and
+ # n relatively prime and n>0. Then for x**y to be exactly
+ # representable (at *any* precision), xc must be the nth power of a
+ # positive integer and xe must be divisible by n. If y is negative
+ # then additionally xc must be a power of either 2 or 5, hence a power
+ # of 2**n or 5**n.
#
# There's a limit to how small |y| can be: if y=m/n as above
# then:
@@ -1946,21 +2029,43 @@ class Decimal(object):
return None
# now xc is a power of 2; e is its exponent
e = _nbits(xc)-1
- # find e*y and xe*y; both must be integers
- if ye >= 0:
- y_as_int = yc*10**ye
- e = e*y_as_int
- xe = xe*y_as_int
- else:
- ten_pow = 10**-ye
- e, remainder = divmod(e*yc, ten_pow)
- if remainder:
- return None
- xe, remainder = divmod(xe*yc, ten_pow)
- if remainder:
- return None
-
- if e*65 >= p*93: # 93/65 > log(10)/log(5)
+
+ # We now have:
+ #
+ # x = 2**e * 10**xe, e > 0, and y < 0.
+ #
+ # The exact result is:
+ #
+ # x**y = 5**(-e*y) * 10**(e*y + xe*y)
+ #
+ # provided that both e*y and xe*y are integers. Note that if
+ # 5**(-e*y) >= 10**p, then the result can't be expressed
+ # exactly with p digits of precision.
+ #
+ # Using the above, we can guard against large values of ye.
+ # 93/65 is an upper bound for log(10)/log(5), so if
+ #
+ # ye >= len(str(93*p//65))
+ #
+ # then
+ #
+ # -e*y >= -y >= 10**ye > 93*p/65 > p*log(10)/log(5),
+ #
+ # so 5**(-e*y) >= 10**p, and the coefficient of the result
+ # can't be expressed in p digits.
+
+ # emax >= largest e such that 5**e < 10**p.
+ emax = p*93//65
+ if ye >= len(str(emax)):
+ return None
+
+ # Find -e*y and -xe*y; both must be integers
+ e = _decimal_lshift_exact(e * yc, ye)
+ xe = _decimal_lshift_exact(xe * yc, ye)
+ if e is None or xe is None:
+ return None
+
+ if e > emax:
return None
xc = 5**e
@@ -1974,19 +2079,20 @@ class Decimal(object):
while xc % 5 == 0:
xc //= 5
e -= 1
- if ye >= 0:
- y_as_integer = yc*10**ye
- e = e*y_as_integer
- xe = xe*y_as_integer
- else:
- ten_pow = 10**-ye
- e, remainder = divmod(e*yc, ten_pow)
- if remainder:
- return None
- xe, remainder = divmod(xe*yc, ten_pow)
- if remainder:
- return None
- if e*3 >= p*10: # 10/3 > log(10)/log(2)
+
+ # Guard against large values of ye, using the same logic as in
+ # the 'xc is a power of 2' branch. 10/3 is an upper bound for
+ # log(10)/log(2).
+ emax = p*10//3
+ if ye >= len(str(emax)):
+ return None
+
+ e = _decimal_lshift_exact(e * yc, ye)
+ xe = _decimal_lshift_exact(xe * yc, ye)
+ if e is None or xe is None:
+ return None
+
+ if e > emax:
return None
xc = 2**e
else:
@@ -2420,8 +2526,8 @@ class Decimal(object):
if digits < 0:
self = _dec_from_triple(self._sign, '1', exp-1)
digits = 0
- this_function = getattr(self, self._pick_rounding_function[rounding])
- changed = this_function(digits)
+ this_function = self._pick_rounding_function[rounding]
+ changed = this_function(self, digits)
coeff = self._int[:digits] or '0'
if changed == 1:
coeff = str(int(coeff)+1)
@@ -2813,6 +2919,7 @@ class Decimal(object):
def copy_sign(self, other):
"""Returns self with the sign of other."""
+ other = _convert_other(other, raiseit=True)
return _dec_from_triple(other._sign, self._int,
self._exp, self._is_special)
@@ -3517,27 +3624,25 @@ class Decimal(object):
return (self.__class__, (str(self),))
def __copy__(self):
- if type(self) == Decimal:
+ if type(self) is Decimal:
return self # I'm immutable; therefore I am my own clone
return self.__class__(str(self))
def __deepcopy__(self, memo):
- if type(self) == Decimal:
+ if type(self) is Decimal:
return self # My components are also immutable
return self.__class__(str(self))
- # PEP 3101 support. See also _parse_format_specifier and _format_align
- def __format__(self, specifier, context=None):
+ # PEP 3101 support. the _localeconv keyword argument should be
+ # considered private: it's provided for ease of testing only.
+ def __format__(self, specifier, context=None, _localeconv=None):
"""Format a Decimal instance according to the given specifier.
The specifier should be a standard format specifier, with the
form described in PEP 3101. Formatting types 'e', 'E', 'f',
- 'F', 'g', 'G', and '%' are supported. If the formatting type
- is omitted it defaults to 'g' or 'G', depending on the value
- of context.capitals.
-
- At this time the 'n' format specifier type (which is supposed
- to use the current locale) is not supported.
+ 'F', 'g', 'G', 'n' and '%' are supported. If the formatting
+ type is omitted it defaults to 'g' or 'G', depending on the
+ value of context.capitals.
"""
# Note: PEP 3101 says that if the type is not present then
@@ -3548,17 +3653,20 @@ class Decimal(object):
if context is None:
context = getcontext()
- spec = _parse_format_specifier(specifier)
+ spec = _parse_format_specifier(specifier, _localeconv=_localeconv)
- # special values don't care about the type or precision...
+ # special values don't care about the type or precision
if self._is_special:
- return _format_align(str(self), spec)
+ sign = _format_sign(self._sign, spec)
+ body = str(self.copy_abs())
+ return _format_align(sign, body, spec)
# a type of None defaults to 'g' or 'G', depending on context
- # if type is '%', adjust exponent of self accordingly
if spec['type'] is None:
spec['type'] = ['g', 'G'][context.capitals]
- elif spec['type'] == '%':
+
+ # if type is '%', adjust exponent of self accordingly
+ if spec['type'] == '%':
self = _dec_from_triple(self._sign, self._int, self._exp+2)
# round if necessary, taking rounding mode from the context
@@ -3567,53 +3675,45 @@ class Decimal(object):
if precision is not None:
if spec['type'] in 'eE':
self = self._round(precision+1, rounding)
- elif spec['type'] in 'gG':
- if len(self._int) > precision:
- self = self._round(precision, rounding)
elif spec['type'] in 'fF%':
self = self._rescale(-precision, rounding)
+ elif spec['type'] in 'gG' and len(self._int) > precision:
+ self = self._round(precision, rounding)
# special case: zeros with a positive exponent can't be
# represented in fixed point; rescale them to 0e0.
- elif not self and self._exp > 0 and spec['type'] in 'fF%':
+ if not self and self._exp > 0 and spec['type'] in 'fF%':
self = self._rescale(0, rounding)
# figure out placement of the decimal point
leftdigits = self._exp + len(self._int)
- if spec['type'] in 'fF%':
- dotplace = leftdigits
- elif spec['type'] in 'eE':
+ if spec['type'] in 'eE':
if not self and precision is not None:
dotplace = 1 - precision
else:
dotplace = 1
+ elif spec['type'] in 'fF%':
+ dotplace = leftdigits
elif spec['type'] in 'gG':
if self._exp <= 0 and leftdigits > -6:
dotplace = leftdigits
else:
dotplace = 1
- # figure out main part of numeric string...
- if dotplace <= 0:
- num = '0.' + '0'*(-dotplace) + self._int
- elif dotplace >= len(self._int):
- # make sure we're not padding a '0' with extra zeros on the right
- assert dotplace==len(self._int) or self._int != '0'
- num = self._int + '0'*(dotplace-len(self._int))
+ # find digits before and after decimal point, and get exponent
+ if dotplace < 0:
+ intpart = '0'
+ fracpart = '0'*(-dotplace) + self._int
+ elif dotplace > len(self._int):
+ intpart = self._int + '0'*(dotplace-len(self._int))
+ fracpart = ''
else:
- num = self._int[:dotplace] + '.' + self._int[dotplace:]
-
- # ...then the trailing exponent, or trailing '%'
- if leftdigits != dotplace or spec['type'] in 'eE':
- echar = {'E': 'E', 'e': 'e', 'G': 'E', 'g': 'e'}[spec['type']]
- num = num + "{0}{1:+}".format(echar, leftdigits-dotplace)
- elif spec['type'] == '%':
- num = num + '%'
-
- # add sign
- if self._sign == 1:
- num = '-' + num
- return _format_align(num, spec)
+ intpart = self._int[:dotplace] or '0'
+ fracpart = self._int[dotplace:]
+ exp = leftdigits-dotplace
+ # done with the decimal-specific stuff; hand over the rest
+ # of the formatting to the _format_number function
+ return _format_number(self._sign, intpart, fracpart, exp, spec)
def _dec_from_triple(sign, coefficient, exponent, special=False):
"""Create a decimal instance directly, without any validation,
@@ -3639,18 +3739,6 @@ _numbers.Number.register(Decimal)
##### Context class #######################################################
-
-# get rounding method function:
-rounding_functions = [name for name in Decimal.__dict__.keys()
- if name.startswith('_round_')]
-for name in rounding_functions:
- # name is like _round_half_even, goes to the global ROUND_HALF_EVEN value.
- globalname = name[1:].upper()
- val = globals()[globalname]
- Decimal._pick_rounding_function[val] = name
-
-del name, val, globalname, rounding_functions
-
class _ContextManager(object):
"""Context manager class to support localcontext().
@@ -3842,6 +3930,23 @@ class Context(object):
"diagnostic info too long in NaN")
return d._fix(self)
+ def create_decimal_from_float(self, f):
+ """Creates a new Decimal instance from a float but rounding using self
+ as the context.
+
+ >>> context = Context(prec=5, rounding=ROUND_DOWN)
+ >>> context.create_decimal_from_float(3.1415926535897932)
+ Decimal('3.1415')
+ >>> context = Context(prec=5, traps=[Inexact])
+ >>> context.create_decimal_from_float(3.1415926535897932)
+ Traceback (most recent call last):
+ ...
+ Inexact: None
+
+ """
+ d = Decimal.from_float(f) # An exact conversion
+ return d._fix(self) # Apply the context rounding
+
# Methods
def abs(self, a):
"""Returns the absolute value of the operand.
@@ -3858,7 +3963,10 @@ class Context(object):
Decimal('101.5')
>>> ExtendedContext.abs(Decimal('-101.5'))
Decimal('101.5')
+ >>> ExtendedContext.abs(-1)
+ Decimal('1')
"""
+ a = _convert_other(a, raiseit=True)
return a.__abs__(context=self)
def add(self, a, b):
@@ -3868,8 +3976,19 @@ class Context(object):
Decimal('19.00')
>>> ExtendedContext.add(Decimal('1E+2'), Decimal('1.01E+4'))
Decimal('1.02E+4')
+ >>> ExtendedContext.add(1, Decimal(2))
+ Decimal('3')
+ >>> ExtendedContext.add(Decimal(8), 5)
+ Decimal('13')
+ >>> ExtendedContext.add(5, 5)
+ Decimal('10')
"""
- return a.__add__(b, context=self)
+ a = _convert_other(a, raiseit=True)
+ r = a.__add__(b, context=self)
+ if r is NotImplemented:
+ raise TypeError("Unable to convert %s to Decimal" % b)
+ else:
+ return r
def _apply(self, a):
return str(a._fix(self))
@@ -3911,7 +4030,14 @@ class Context(object):
Decimal('1')
>>> ExtendedContext.compare(Decimal('-3'), Decimal('2.1'))
Decimal('-1')
+ >>> ExtendedContext.compare(1, 2)
+ Decimal('-1')
+ >>> ExtendedContext.compare(Decimal(1), 2)
+ Decimal('-1')
+ >>> ExtendedContext.compare(1, Decimal(2))
+ Decimal('-1')
"""
+ a = _convert_other(a, raiseit=True)
return a.compare(b, context=self)
def compare_signal(self, a, b):
@@ -3939,7 +4065,14 @@ class Context(object):
Decimal('NaN')
>>> print c.flags[InvalidOperation]
1
+ >>> c.compare_signal(-1, 2)
+ Decimal('-1')
+ >>> c.compare_signal(Decimal(-1), 2)
+ Decimal('-1')
+ >>> c.compare_signal(-1, Decimal(2))
+ Decimal('-1')
"""
+ a = _convert_other(a, raiseit=True)
return a.compare_signal(b, context=self)
def compare_total(self, a, b):
@@ -3961,7 +4094,14 @@ class Context(object):
Decimal('1')
>>> ExtendedContext.compare_total(Decimal('12.3'), Decimal('NaN'))
Decimal('-1')
+ >>> ExtendedContext.compare_total(1, 2)
+ Decimal('-1')
+ >>> ExtendedContext.compare_total(Decimal(1), 2)
+ Decimal('-1')
+ >>> ExtendedContext.compare_total(1, Decimal(2))
+ Decimal('-1')
"""
+ a = _convert_other(a, raiseit=True)
return a.compare_total(b)
def compare_total_mag(self, a, b):
@@ -3969,6 +4109,7 @@ class Context(object):
Like compare_total, but with operand's sign ignored and assumed to be 0.
"""
+ a = _convert_other(a, raiseit=True)
return a.compare_total_mag(b)
def copy_abs(self, a):
@@ -3978,17 +4119,23 @@ class Context(object):
Decimal('2.1')
>>> ExtendedContext.copy_abs(Decimal('-100'))
Decimal('100')
+ >>> ExtendedContext.copy_abs(-1)
+ Decimal('1')
"""
+ a = _convert_other(a, raiseit=True)
return a.copy_abs()
def copy_decimal(self, a):
- """Returns a copy of the decimal objet.
+ """Returns a copy of the decimal object.
>>> ExtendedContext.copy_decimal(Decimal('2.1'))
Decimal('2.1')
>>> ExtendedContext.copy_decimal(Decimal('-1.00'))
Decimal('-1.00')
+ >>> ExtendedContext.copy_decimal(1)
+ Decimal('1')
"""
+ a = _convert_other(a, raiseit=True)
return Decimal(a)
def copy_negate(self, a):
@@ -3998,7 +4145,10 @@ class Context(object):
Decimal('-101.5')
>>> ExtendedContext.copy_negate(Decimal('-101.5'))
Decimal('101.5')
+ >>> ExtendedContext.copy_negate(1)
+ Decimal('-1')
"""
+ a = _convert_other(a, raiseit=True)
return a.copy_negate()
def copy_sign(self, a, b):
@@ -4015,7 +4165,14 @@ class Context(object):
Decimal('-1.50')
>>> ExtendedContext.copy_sign(Decimal('-1.50'), Decimal('-7.33'))
Decimal('-1.50')
+ >>> ExtendedContext.copy_sign(1, -2)
+ Decimal('-1')
+ >>> ExtendedContext.copy_sign(Decimal(1), -2)
+ Decimal('-1')
+ >>> ExtendedContext.copy_sign(1, Decimal(-2))
+ Decimal('-1')
"""
+ a = _convert_other(a, raiseit=True)
return a.copy_sign(b)
def divide(self, a, b):
@@ -4041,8 +4198,19 @@ class Context(object):
Decimal('1000')
>>> ExtendedContext.divide(Decimal('2.40E+6'), Decimal('2'))
Decimal('1.20E+6')
+ >>> ExtendedContext.divide(5, 5)
+ Decimal('1')
+ >>> ExtendedContext.divide(Decimal(5), 5)
+ Decimal('1')
+ >>> ExtendedContext.divide(5, Decimal(5))
+ Decimal('1')
"""
- return a.__div__(b, context=self)
+ a = _convert_other(a, raiseit=True)
+ r = a.__div__(b, context=self)
+ if r is NotImplemented:
+ raise TypeError("Unable to convert %s to Decimal" % b)
+ else:
+ return r
def divide_int(self, a, b):
"""Divides two numbers and returns the integer part of the result.
@@ -4053,18 +4221,40 @@ class Context(object):
Decimal('3')
>>> ExtendedContext.divide_int(Decimal('1'), Decimal('0.3'))
Decimal('3')
+ >>> ExtendedContext.divide_int(10, 3)
+ Decimal('3')
+ >>> ExtendedContext.divide_int(Decimal(10), 3)
+ Decimal('3')
+ >>> ExtendedContext.divide_int(10, Decimal(3))
+ Decimal('3')
"""
- return a.__floordiv__(b, context=self)
+ a = _convert_other(a, raiseit=True)
+ r = a.__floordiv__(b, context=self)
+ if r is NotImplemented:
+ raise TypeError("Unable to convert %s to Decimal" % b)
+ else:
+ return r
def divmod(self, a, b):
- """Return (a // b, a % b)
+ """Return (a // b, a % b).
>>> ExtendedContext.divmod(Decimal(8), Decimal(3))
(Decimal('2'), Decimal('2'))
>>> ExtendedContext.divmod(Decimal(8), Decimal(4))
(Decimal('2'), Decimal('0'))
+ >>> ExtendedContext.divmod(8, 4)
+ (Decimal('2'), Decimal('0'))
+ >>> ExtendedContext.divmod(Decimal(8), 4)
+ (Decimal('2'), Decimal('0'))
+ >>> ExtendedContext.divmod(8, Decimal(4))
+ (Decimal('2'), Decimal('0'))
"""
- return a.__divmod__(b, context=self)
+ a = _convert_other(a, raiseit=True)
+ r = a.__divmod__(b, context=self)
+ if r is NotImplemented:
+ raise TypeError("Unable to convert %s to Decimal" % b)
+ else:
+ return r
def exp(self, a):
"""Returns e ** a.
@@ -4084,7 +4274,10 @@ class Context(object):
Decimal('2.00000000')
>>> c.exp(Decimal('+Infinity'))
Decimal('Infinity')
+ >>> c.exp(10)
+ Decimal('22026.4658')
"""
+ a =_convert_other(a, raiseit=True)
return a.exp(context=self)
def fma(self, a, b, c):
@@ -4100,7 +4293,14 @@ class Context(object):
Decimal('-8')
>>> ExtendedContext.fma(Decimal('888565290'), Decimal('1557.96930'), Decimal('-86087.7578'))
Decimal('1.38435736E+12')
+ >>> ExtendedContext.fma(1, 3, 4)
+ Decimal('7')
+ >>> ExtendedContext.fma(1, Decimal(3), 4)
+ Decimal('7')
+ >>> ExtendedContext.fma(1, 3, Decimal(4))
+ Decimal('7')
"""
+ a = _convert_other(a, raiseit=True)
return a.fma(b, c, context=self)
def is_canonical(self, a):
@@ -4130,7 +4330,10 @@ class Context(object):
False
>>> ExtendedContext.is_finite(Decimal('NaN'))
False
+ >>> ExtendedContext.is_finite(1)
+ True
"""
+ a = _convert_other(a, raiseit=True)
return a.is_finite()
def is_infinite(self, a):
@@ -4142,7 +4345,10 @@ class Context(object):
True
>>> ExtendedContext.is_infinite(Decimal('NaN'))
False
+ >>> ExtendedContext.is_infinite(1)
+ False
"""
+ a = _convert_other(a, raiseit=True)
return a.is_infinite()
def is_nan(self, a):
@@ -4155,7 +4361,10 @@ class Context(object):
True
>>> ExtendedContext.is_nan(Decimal('-sNaN'))
True
+ >>> ExtendedContext.is_nan(1)
+ False
"""
+ a = _convert_other(a, raiseit=True)
return a.is_nan()
def is_normal(self, a):
@@ -4175,7 +4384,10 @@ class Context(object):
False
>>> c.is_normal(Decimal('NaN'))
False
+ >>> c.is_normal(1)
+ True
"""
+ a = _convert_other(a, raiseit=True)
return a.is_normal(context=self)
def is_qnan(self, a):
@@ -4187,7 +4399,10 @@ class Context(object):
True
>>> ExtendedContext.is_qnan(Decimal('sNaN'))
False
+ >>> ExtendedContext.is_qnan(1)
+ False
"""
+ a = _convert_other(a, raiseit=True)
return a.is_qnan()
def is_signed(self, a):
@@ -4199,7 +4414,12 @@ class Context(object):
True
>>> ExtendedContext.is_signed(Decimal('-0'))
True
+ >>> ExtendedContext.is_signed(8)
+ False
+ >>> ExtendedContext.is_signed(-8)
+ True
"""
+ a = _convert_other(a, raiseit=True)
return a.is_signed()
def is_snan(self, a):
@@ -4212,7 +4432,10 @@ class Context(object):
False
>>> ExtendedContext.is_snan(Decimal('sNaN'))
True
+ >>> ExtendedContext.is_snan(1)
+ False
"""
+ a = _convert_other(a, raiseit=True)
return a.is_snan()
def is_subnormal(self, a):
@@ -4231,7 +4454,10 @@ class Context(object):
False
>>> c.is_subnormal(Decimal('NaN'))
False
+ >>> c.is_subnormal(1)
+ False
"""
+ a = _convert_other(a, raiseit=True)
return a.is_subnormal(context=self)
def is_zero(self, a):
@@ -4243,7 +4469,12 @@ class Context(object):
False
>>> ExtendedContext.is_zero(Decimal('-0E+2'))
True
+ >>> ExtendedContext.is_zero(1)
+ False
+ >>> ExtendedContext.is_zero(0)
+ True
"""
+ a = _convert_other(a, raiseit=True)
return a.is_zero()
def ln(self, a):
@@ -4262,7 +4493,10 @@ class Context(object):
Decimal('2.30258509')
>>> c.ln(Decimal('+Infinity'))
Decimal('Infinity')
+ >>> c.ln(1)
+ Decimal('0')
"""
+ a = _convert_other(a, raiseit=True)
return a.ln(context=self)
def log10(self, a):
@@ -4285,7 +4519,12 @@ class Context(object):
Decimal('1.84509804')
>>> c.log10(Decimal('+Infinity'))
Decimal('Infinity')
+ >>> c.log10(0)
+ Decimal('-Infinity')
+ >>> c.log10(1)
+ Decimal('0')
"""
+ a = _convert_other(a, raiseit=True)
return a.log10(context=self)
def logb(self, a):
@@ -4304,7 +4543,14 @@ class Context(object):
Decimal('-2')
>>> ExtendedContext.logb(Decimal('0'))
Decimal('-Infinity')
+ >>> ExtendedContext.logb(1)
+ Decimal('0')
+ >>> ExtendedContext.logb(10)
+ Decimal('1')
+ >>> ExtendedContext.logb(100)
+ Decimal('2')
"""
+ a = _convert_other(a, raiseit=True)
return a.logb(context=self)
def logical_and(self, a, b):
@@ -4324,7 +4570,14 @@ class Context(object):
Decimal('1000')
>>> ExtendedContext.logical_and(Decimal('1111'), Decimal('10'))
Decimal('10')
+ >>> ExtendedContext.logical_and(110, 1101)
+ Decimal('100')
+ >>> ExtendedContext.logical_and(Decimal(110), 1101)
+ Decimal('100')
+ >>> ExtendedContext.logical_and(110, Decimal(1101))
+ Decimal('100')
"""
+ a = _convert_other(a, raiseit=True)
return a.logical_and(b, context=self)
def logical_invert(self, a):
@@ -4340,7 +4593,10 @@ class Context(object):
Decimal('0')
>>> ExtendedContext.logical_invert(Decimal('101010101'))
Decimal('10101010')
+ >>> ExtendedContext.logical_invert(1101)
+ Decimal('111110010')
"""
+ a = _convert_other(a, raiseit=True)
return a.logical_invert(context=self)
def logical_or(self, a, b):
@@ -4360,7 +4616,14 @@ class Context(object):
Decimal('1110')
>>> ExtendedContext.logical_or(Decimal('1110'), Decimal('10'))
Decimal('1110')
+ >>> ExtendedContext.logical_or(110, 1101)
+ Decimal('1111')
+ >>> ExtendedContext.logical_or(Decimal(110), 1101)
+ Decimal('1111')
+ >>> ExtendedContext.logical_or(110, Decimal(1101))
+ Decimal('1111')
"""
+ a = _convert_other(a, raiseit=True)
return a.logical_or(b, context=self)
def logical_xor(self, a, b):
@@ -4380,10 +4643,17 @@ class Context(object):
Decimal('110')
>>> ExtendedContext.logical_xor(Decimal('1111'), Decimal('10'))
Decimal('1101')
+ >>> ExtendedContext.logical_xor(110, 1101)
+ Decimal('1011')
+ >>> ExtendedContext.logical_xor(Decimal(110), 1101)
+ Decimal('1011')
+ >>> ExtendedContext.logical_xor(110, Decimal(1101))
+ Decimal('1011')
"""
+ a = _convert_other(a, raiseit=True)
return a.logical_xor(b, context=self)
- def max(self, a,b):
+ def max(self, a, b):
"""max compares two values numerically and returns the maximum.
If either operand is a NaN then the general rules apply.
@@ -4400,14 +4670,34 @@ class Context(object):
Decimal('1')
>>> ExtendedContext.max(Decimal('7'), Decimal('NaN'))
Decimal('7')
+ >>> ExtendedContext.max(1, 2)
+ Decimal('2')
+ >>> ExtendedContext.max(Decimal(1), 2)
+ Decimal('2')
+ >>> ExtendedContext.max(1, Decimal(2))
+ Decimal('2')
"""
+ a = _convert_other(a, raiseit=True)
return a.max(b, context=self)
def max_mag(self, a, b):
- """Compares the values numerically with their sign ignored."""
+ """Compares the values numerically with their sign ignored.
+
+ >>> ExtendedContext.max_mag(Decimal('7'), Decimal('NaN'))
+ Decimal('7')
+ >>> ExtendedContext.max_mag(Decimal('7'), Decimal('-10'))
+ Decimal('-10')
+ >>> ExtendedContext.max_mag(1, -2)
+ Decimal('-2')
+ >>> ExtendedContext.max_mag(Decimal(1), -2)
+ Decimal('-2')
+ >>> ExtendedContext.max_mag(1, Decimal(-2))
+ Decimal('-2')
+ """
+ a = _convert_other(a, raiseit=True)
return a.max_mag(b, context=self)
- def min(self, a,b):
+ def min(self, a, b):
"""min compares two values numerically and returns the minimum.
If either operand is a NaN then the general rules apply.
@@ -4424,11 +4714,31 @@ class Context(object):
Decimal('1.0')
>>> ExtendedContext.min(Decimal('7'), Decimal('NaN'))
Decimal('7')
+ >>> ExtendedContext.min(1, 2)
+ Decimal('1')
+ >>> ExtendedContext.min(Decimal(1), 2)
+ Decimal('1')
+ >>> ExtendedContext.min(1, Decimal(29))
+ Decimal('1')
"""
+ a = _convert_other(a, raiseit=True)
return a.min(b, context=self)
def min_mag(self, a, b):
- """Compares the values numerically with their sign ignored."""
+ """Compares the values numerically with their sign ignored.
+
+ >>> ExtendedContext.min_mag(Decimal('3'), Decimal('-2'))
+ Decimal('-2')
+ >>> ExtendedContext.min_mag(Decimal('-3'), Decimal('NaN'))
+ Decimal('-3')
+ >>> ExtendedContext.min_mag(1, -2)
+ Decimal('1')
+ >>> ExtendedContext.min_mag(Decimal(1), -2)
+ Decimal('1')
+ >>> ExtendedContext.min_mag(1, Decimal(-2))
+ Decimal('1')
+ """
+ a = _convert_other(a, raiseit=True)
return a.min_mag(b, context=self)
def minus(self, a):
@@ -4442,16 +4752,19 @@ class Context(object):
Decimal('-1.3')
>>> ExtendedContext.minus(Decimal('-1.3'))
Decimal('1.3')
+ >>> ExtendedContext.minus(1)
+ Decimal('-1')
"""
+ a = _convert_other(a, raiseit=True)
return a.__neg__(context=self)
def multiply(self, a, b):
"""multiply multiplies two operands.
If either operand is a special value then the general rules apply.
- Otherwise, the operands are multiplied together ('long multiplication'),
- resulting in a number which may be as long as the sum of the lengths
- of the two operands.
+ Otherwise, the operands are multiplied together
+ ('long multiplication'), resulting in a number which may be as long as
+ the sum of the lengths of the two operands.
>>> ExtendedContext.multiply(Decimal('1.20'), Decimal('3'))
Decimal('3.60')
@@ -4463,8 +4776,19 @@ class Context(object):
Decimal('-0.0')
>>> ExtendedContext.multiply(Decimal('654321'), Decimal('654321'))
Decimal('4.28135971E+11')
+ >>> ExtendedContext.multiply(7, 7)
+ Decimal('49')
+ >>> ExtendedContext.multiply(Decimal(7), 7)
+ Decimal('49')
+ >>> ExtendedContext.multiply(7, Decimal(7))
+ Decimal('49')
"""
- return a.__mul__(b, context=self)
+ a = _convert_other(a, raiseit=True)
+ r = a.__mul__(b, context=self)
+ if r is NotImplemented:
+ raise TypeError("Unable to convert %s to Decimal" % b)
+ else:
+ return r
def next_minus(self, a):
"""Returns the largest representable number smaller than a.
@@ -4480,7 +4804,10 @@ class Context(object):
Decimal('-1.00000004')
>>> c.next_minus(Decimal('Infinity'))
Decimal('9.99999999E+999')
+ >>> c.next_minus(1)
+ Decimal('0.999999999')
"""
+ a = _convert_other(a, raiseit=True)
return a.next_minus(context=self)
def next_plus(self, a):
@@ -4497,7 +4824,10 @@ class Context(object):
Decimal('-1.00000002')
>>> c.next_plus(Decimal('-Infinity'))
Decimal('-9.99999999E+999')
+ >>> c.next_plus(1)
+ Decimal('1.00000001')
"""
+ a = _convert_other(a, raiseit=True)
return a.next_plus(context=self)
def next_toward(self, a, b):
@@ -4525,7 +4855,14 @@ class Context(object):
Decimal('-1.00000004')
>>> c.next_toward(Decimal('0.00'), Decimal('-0.0000'))
Decimal('-0.00')
+ >>> c.next_toward(0, 1)
+ Decimal('1E-1007')
+ >>> c.next_toward(Decimal(0), 1)
+ Decimal('1E-1007')
+ >>> c.next_toward(0, Decimal(1))
+ Decimal('1E-1007')
"""
+ a = _convert_other(a, raiseit=True)
return a.next_toward(b, context=self)
def normalize(self, a):
@@ -4546,7 +4883,10 @@ class Context(object):
Decimal('1.2E+2')
>>> ExtendedContext.normalize(Decimal('0.00'))
Decimal('0')
+ >>> ExtendedContext.normalize(6)
+ Decimal('6')
"""
+ a = _convert_other(a, raiseit=True)
return a.normalize(context=self)
def number_class(self, a):
@@ -4593,7 +4933,10 @@ class Context(object):
'NaN'
>>> c.number_class(Decimal('sNaN'))
'sNaN'
+ >>> c.number_class(123)
+ '+Normal'
"""
+ a = _convert_other(a, raiseit=True)
return a.number_class(context=self)
def plus(self, a):
@@ -4607,7 +4950,10 @@ class Context(object):
Decimal('1.3')
>>> ExtendedContext.plus(Decimal('-1.3'))
Decimal('-1.3')
+ >>> ExtendedContext.plus(-1)
+ Decimal('-1')
"""
+ a = _convert_other(a, raiseit=True)
return a.__pos__(context=self)
def power(self, a, b, modulo=None):
@@ -4676,8 +5022,19 @@ class Context(object):
Decimal('-0')
>>> c.power(Decimal('-23'), Decimal('0'), Decimal('65537'))
Decimal('1')
+ >>> ExtendedContext.power(7, 7)
+ Decimal('823543')
+ >>> ExtendedContext.power(Decimal(7), 7)
+ Decimal('823543')
+ >>> ExtendedContext.power(7, Decimal(7), 2)
+ Decimal('1')
"""
- return a.__pow__(b, modulo, context=self)
+ a = _convert_other(a, raiseit=True)
+ r = a.__pow__(b, modulo, context=self)
+ if r is NotImplemented:
+ raise TypeError("Unable to convert %s to Decimal" % b)
+ else:
+ return r
def quantize(self, a, b):
"""Returns a value equal to 'a' (rounded), having the exponent of 'b'.
@@ -4727,7 +5084,14 @@ class Context(object):
Decimal('2.2E+2')
>>> ExtendedContext.quantize(Decimal('217'), Decimal('1e+2'))
Decimal('2E+2')
+ >>> ExtendedContext.quantize(1, 2)
+ Decimal('1')
+ >>> ExtendedContext.quantize(Decimal(1), 2)
+ Decimal('1')
+ >>> ExtendedContext.quantize(1, Decimal(2))
+ Decimal('1')
"""
+ a = _convert_other(a, raiseit=True)
return a.quantize(b, context=self)
def radix(self):
@@ -4762,8 +5126,19 @@ class Context(object):
Decimal('0.1')
>>> ExtendedContext.remainder(Decimal('3.6'), Decimal('1.3'))
Decimal('1.0')
+ >>> ExtendedContext.remainder(22, 6)
+ Decimal('4')
+ >>> ExtendedContext.remainder(Decimal(22), 6)
+ Decimal('4')
+ >>> ExtendedContext.remainder(22, Decimal(6))
+ Decimal('4')
"""
- return a.__mod__(b, context=self)
+ a = _convert_other(a, raiseit=True)
+ r = a.__mod__(b, context=self)
+ if r is NotImplemented:
+ raise TypeError("Unable to convert %s to Decimal" % b)
+ else:
+ return r
def remainder_near(self, a, b):
"""Returns to be "a - b * n", where n is the integer nearest the exact
@@ -4789,7 +5164,14 @@ class Context(object):
Decimal('0.1')
>>> ExtendedContext.remainder_near(Decimal('3.6'), Decimal('1.3'))
Decimal('-0.3')
+ >>> ExtendedContext.remainder_near(3, 11)
+ Decimal('3')
+ >>> ExtendedContext.remainder_near(Decimal(3), 11)
+ Decimal('3')
+ >>> ExtendedContext.remainder_near(3, Decimal(11))
+ Decimal('3')
"""
+ a = _convert_other(a, raiseit=True)
return a.remainder_near(b, context=self)
def rotate(self, a, b):
@@ -4811,7 +5193,14 @@ class Context(object):
Decimal('123456789')
>>> ExtendedContext.rotate(Decimal('123456789'), Decimal('+2'))
Decimal('345678912')
+ >>> ExtendedContext.rotate(1333333, 1)
+ Decimal('13333330')
+ >>> ExtendedContext.rotate(Decimal(1333333), 1)
+ Decimal('13333330')
+ >>> ExtendedContext.rotate(1333333, Decimal(1))
+ Decimal('13333330')
"""
+ a = _convert_other(a, raiseit=True)
return a.rotate(b, context=self)
def same_quantum(self, a, b):
@@ -4828,7 +5217,14 @@ class Context(object):
False
>>> ExtendedContext.same_quantum(Decimal('Inf'), Decimal('-Inf'))
True
+ >>> ExtendedContext.same_quantum(10000, -1)
+ True
+ >>> ExtendedContext.same_quantum(Decimal(10000), -1)
+ True
+ >>> ExtendedContext.same_quantum(10000, Decimal(-1))
+ True
"""
+ a = _convert_other(a, raiseit=True)
return a.same_quantum(b)
def scaleb (self, a, b):
@@ -4840,8 +5236,15 @@ class Context(object):
Decimal('7.50')
>>> ExtendedContext.scaleb(Decimal('7.50'), Decimal('3'))
Decimal('7.50E+3')
+ >>> ExtendedContext.scaleb(1, 4)
+ Decimal('1E+4')
+ >>> ExtendedContext.scaleb(Decimal(1), 4)
+ Decimal('1E+4')
+ >>> ExtendedContext.scaleb(1, Decimal(4))
+ Decimal('1E+4')
"""
- return a.scaleb (b, context=self)
+ a = _convert_other(a, raiseit=True)
+ return a.scaleb(b, context=self)
def shift(self, a, b):
"""Returns a shifted copy of a, b times.
@@ -4863,7 +5266,14 @@ class Context(object):
Decimal('123456789')
>>> ExtendedContext.shift(Decimal('123456789'), Decimal('+2'))
Decimal('345678900')
+ >>> ExtendedContext.shift(88888888, 2)
+ Decimal('888888800')
+ >>> ExtendedContext.shift(Decimal(88888888), 2)
+ Decimal('888888800')
+ >>> ExtendedContext.shift(88888888, Decimal(2))
+ Decimal('888888800')
"""
+ a = _convert_other(a, raiseit=True)
return a.shift(b, context=self)
def sqrt(self, a):
@@ -4890,9 +5300,12 @@ class Context(object):
Decimal('2.64575131')
>>> ExtendedContext.sqrt(Decimal('10'))
Decimal('3.16227766')
+ >>> ExtendedContext.sqrt(2)
+ Decimal('1.41421356')
>>> ExtendedContext.prec
9
"""
+ a = _convert_other(a, raiseit=True)
return a.sqrt(context=self)
def subtract(self, a, b):
@@ -4904,14 +5317,26 @@ class Context(object):
Decimal('0.00')
>>> ExtendedContext.subtract(Decimal('1.3'), Decimal('2.07'))
Decimal('-0.77')
+ >>> ExtendedContext.subtract(8, 5)
+ Decimal('3')
+ >>> ExtendedContext.subtract(Decimal(8), 5)
+ Decimal('3')
+ >>> ExtendedContext.subtract(8, Decimal(5))
+ Decimal('3')
"""
- return a.__sub__(b, context=self)
+ a = _convert_other(a, raiseit=True)
+ r = a.__sub__(b, context=self)
+ if r is NotImplemented:
+ raise TypeError("Unable to convert %s to Decimal" % b)
+ else:
+ return r
def to_eng_string(self, a):
"""Converts a number to a string, using scientific notation.
The operation is not affected by the context.
"""
+ a = _convert_other(a, raiseit=True)
return a.to_eng_string(context=self)
def to_sci_string(self, a):
@@ -4919,6 +5344,7 @@ class Context(object):
The operation is not affected by the context.
"""
+ a = _convert_other(a, raiseit=True)
return a.__str__(context=self)
def to_integral_exact(self, a):
@@ -4948,6 +5374,7 @@ class Context(object):
>>> ExtendedContext.to_integral_exact(Decimal('-Inf'))
Decimal('-Infinity')
"""
+ a = _convert_other(a, raiseit=True)
return a.to_integral_exact(context=self)
def to_integral_value(self, a):
@@ -4976,6 +5403,7 @@ class Context(object):
>>> ExtendedContext.to_integral_value(Decimal('-Inf'))
Decimal('-Infinity')
"""
+ a = _convert_other(a, raiseit=True)
return a.to_integral_value(context=self)
# the method name changed, but we provide also the old one, for compatibility
@@ -5057,6 +5485,27 @@ def _nbits(n, correction = {
hex_n = "%x" % n
return 4*len(hex_n) - correction[hex_n[0]]
+def _decimal_lshift_exact(n, e):
+ """ Given integers n and e, return n * 10**e if it's an integer, else None.
+
+ The computation is designed to avoid computing large powers of 10
+ unnecessarily.
+
+ >>> _decimal_lshift_exact(3, 4)
+ 30000
+ >>> _decimal_lshift_exact(300, -999999999) # returns None
+
+ """
+ if n == 0:
+ return 0
+ elif e >= 0:
+ return n * 10**e
+ else:
+ # val_n = largest power of 10 dividing n.
+ str_n = str(abs(n))
+ val_n = len(str_n) - len(str_n.rstrip('0'))
+ return None if val_n < -e else n // 10**-e
+
def _sqrt_nearest(n, a):
"""Closest integer to the square root of the positive integer n. a is
an initial approximation to the square root. Any positive integer
@@ -5377,15 +5826,21 @@ def _log10_lb(c, correction = {
##### Helper Functions ####################################################
-def _convert_other(other, raiseit=False):
+def _convert_other(other, raiseit=False, allow_float=False):
"""Convert other to Decimal.
Verifies that it's ok to use in an implicit construction.
+ If allow_float is true, allow conversion from float; this
+ is used in the comparison methods (__eq__ and friends).
+
"""
if isinstance(other, Decimal):
return other
if isinstance(other, (int, long)):
return Decimal(other)
+ if allow_float and isinstance(other, float):
+ return Decimal.from_float(other)
+
if raiseit:
raise TypeError("Unable to convert %s to Decimal" % other)
return NotImplemented
@@ -5460,14 +5915,13 @@ _all_zeros = re.compile('0*$').match
_exact_half = re.compile('50*$').match
##### PEP3101 support functions ##############################################
-# The functions parse_format_specifier and format_align have little to do
-# with the Decimal class, and could potentially be reused for other pure
+# The functions in this section have little to do with the Decimal
+# class, and could potentially be reused or adapted for other pure
# Python numeric classes that want to implement __format__
#
# A format specifier for Decimal looks like:
#
-# [[fill]align][sign][0][minimumwidth][.precision][type]
-#
+# [[fill]align][sign][0][minimumwidth][,][.precision][type]
_parse_format_specifier_regex = re.compile(r"""\A
(?:
@@ -5477,14 +5931,23 @@ _parse_format_specifier_regex = re.compile(r"""\A
(?P<sign>[-+ ])?
(?P<zeropad>0)?
(?P<minimumwidth>(?!0)\d+)?
+(?P<thousands_sep>,)?
(?:\.(?P<precision>0|(?!0)\d+))?
-(?P<type>[eEfFgG%])?
+(?P<type>[eEfFgGn%])?
\Z
""", re.VERBOSE)
del re
-def _parse_format_specifier(format_spec):
+# The locale module is only needed for the 'n' format specifier. The
+# rest of the PEP 3101 code functions quite happily without it, so we
+# don't care too much if locale isn't present.
+try:
+ import locale as _locale
+except ImportError:
+ pass
+
+def _parse_format_specifier(format_spec, _localeconv=None):
"""Parse and validate a format specifier.
Turns a standard numeric format specifier into a dict, with the
@@ -5494,9 +5957,14 @@ def _parse_format_specifier(format_spec):
align: alignment type, either '<', '>', '=' or '^'
sign: either '+', '-' or ' '
minimumwidth: nonnegative integer giving minimum width
+ zeropad: boolean, indicating whether to pad with zeros
+ thousands_sep: string to use as thousands separator, or ''
+ grouping: grouping for thousands separators, in format
+ used by localeconv
+ decimal_point: string to use for decimal point
precision: nonnegative integer giving precision, or None
type: one of the characters 'eEfFgG%', or None
- unicode: either True or False (always True for Python 3.x)
+ unicode: boolean (always True for Python 3.x)
"""
m = _parse_format_specifier_regex.match(format_spec)
@@ -5506,26 +5974,28 @@ def _parse_format_specifier(format_spec):
# get the dictionary
format_dict = m.groupdict()
- # defaults for fill and alignment
+ # zeropad; defaults for fill and alignment. If zero padding
+ # is requested, the fill and align fields should be absent.
fill = format_dict['fill']
align = format_dict['align']
- if format_dict.pop('zeropad') is not None:
- # in the face of conflict, refuse the temptation to guess
- if fill is not None and fill != '0':
+ format_dict['zeropad'] = (format_dict['zeropad'] is not None)
+ if format_dict['zeropad']:
+ if fill is not None:
raise ValueError("Fill character conflicts with '0'"
" in format specifier: " + format_spec)
- if align is not None and align != '=':
+ if align is not None:
raise ValueError("Alignment conflicts with '0' in "
"format specifier: " + format_spec)
- fill = '0'
- align = '='
format_dict['fill'] = fill or ' '
- format_dict['align'] = align or '<'
+ # PEP 3101 originally specified that the default alignment should
+ # be left; it was later agreed that right-aligned makes more sense
+ # for numeric types. See http://bugs.python.org/issue6857.
+ format_dict['align'] = align or '>'
+ # default sign handling: '-' for negative, '' for positive
if format_dict['sign'] is None:
format_dict['sign'] = '-'
- # turn minimumwidth and precision entries into integers.
# minimumwidth defaults to 0; precision remains None if not given
format_dict['minimumwidth'] = int(format_dict['minimumwidth'] or '0')
if format_dict['precision'] is not None:
@@ -5537,58 +6007,171 @@ def _parse_format_specifier(format_spec):
if format_dict['type'] is None or format_dict['type'] in 'gG':
format_dict['precision'] = 1
+ # determine thousands separator, grouping, and decimal separator, and
+ # add appropriate entries to format_dict
+ if format_dict['type'] == 'n':
+ # apart from separators, 'n' behaves just like 'g'
+ format_dict['type'] = 'g'
+ if _localeconv is None:
+ _localeconv = _locale.localeconv()
+ if format_dict['thousands_sep'] is not None:
+ raise ValueError("Explicit thousands separator conflicts with "
+ "'n' type in format specifier: " + format_spec)
+ format_dict['thousands_sep'] = _localeconv['thousands_sep']
+ format_dict['grouping'] = _localeconv['grouping']
+ format_dict['decimal_point'] = _localeconv['decimal_point']
+ else:
+ if format_dict['thousands_sep'] is None:
+ format_dict['thousands_sep'] = ''
+ format_dict['grouping'] = [3, 0]
+ format_dict['decimal_point'] = '.'
+
# record whether return type should be str or unicode
format_dict['unicode'] = isinstance(format_spec, unicode)
return format_dict
-def _format_align(body, spec_dict):
- """Given an unpadded, non-aligned numeric string, add padding and
- aligment to conform with the given format specifier dictionary (as
- output from parse_format_specifier).
+def _format_align(sign, body, spec):
+ """Given an unpadded, non-aligned numeric string 'body' and sign
+ string 'sign', add padding and alignment conforming to the given
+ format specifier dictionary 'spec' (as produced by
+ parse_format_specifier).
- It's assumed that if body is negative then it starts with '-'.
- Any leading sign ('-' or '+') is stripped from the body before
- applying the alignment and padding rules, and replaced in the
- appropriate position.
+ Also converts result to unicode if necessary.
"""
- # figure out the sign; we only examine the first character, so if
- # body has leading whitespace the results may be surprising.
- if len(body) > 0 and body[0] in '-+':
- sign = body[0]
- body = body[1:]
- else:
- sign = ''
-
- if sign != '-':
- if spec_dict['sign'] in ' +':
- sign = spec_dict['sign']
- else:
- sign = ''
-
# how much extra space do we have to play with?
- minimumwidth = spec_dict['minimumwidth']
- fill = spec_dict['fill']
- padding = fill*(max(minimumwidth - (len(sign+body)), 0))
+ minimumwidth = spec['minimumwidth']
+ fill = spec['fill']
+ padding = fill*(minimumwidth - len(sign) - len(body))
- align = spec_dict['align']
+ align = spec['align']
if align == '<':
result = sign + body + padding
elif align == '>':
result = padding + sign + body
elif align == '=':
result = sign + padding + body
- else: #align == '^'
+ elif align == '^':
half = len(padding)//2
result = padding[:half] + sign + body + padding[half:]
+ else:
+ raise ValueError('Unrecognised alignment field')
# make sure that result is unicode if necessary
- if spec_dict['unicode']:
+ if spec['unicode']:
result = unicode(result)
return result
+def _group_lengths(grouping):
+ """Convert a localeconv-style grouping into a (possibly infinite)
+ iterable of integers representing group lengths.
+
+ """
+ # The result from localeconv()['grouping'], and the input to this
+ # function, should be a list of integers in one of the
+ # following three forms:
+ #
+ # (1) an empty list, or
+ # (2) nonempty list of positive integers + [0]
+ # (3) list of positive integers + [locale.CHAR_MAX], or
+
+ from itertools import chain, repeat
+ if not grouping:
+ return []
+ elif grouping[-1] == 0 and len(grouping) >= 2:
+ return chain(grouping[:-1], repeat(grouping[-2]))
+ elif grouping[-1] == _locale.CHAR_MAX:
+ return grouping[:-1]
+ else:
+ raise ValueError('unrecognised format for grouping')
+
+def _insert_thousands_sep(digits, spec, min_width=1):
+ """Insert thousands separators into a digit string.
+
+ spec is a dictionary whose keys should include 'thousands_sep' and
+ 'grouping'; typically it's the result of parsing the format
+ specifier using _parse_format_specifier.
+
+ The min_width keyword argument gives the minimum length of the
+ result, which will be padded on the left with zeros if necessary.
+
+ If necessary, the zero padding adds an extra '0' on the left to
+ avoid a leading thousands separator. For example, inserting
+ commas every three digits in '123456', with min_width=8, gives
+ '0,123,456', even though that has length 9.
+
+ """
+
+ sep = spec['thousands_sep']
+ grouping = spec['grouping']
+
+ groups = []
+ for l in _group_lengths(grouping):
+ if l <= 0:
+ raise ValueError("group length should be positive")
+ # max(..., 1) forces at least 1 digit to the left of a separator
+ l = min(max(len(digits), min_width, 1), l)
+ groups.append('0'*(l - len(digits)) + digits[-l:])
+ digits = digits[:-l]
+ min_width -= l
+ if not digits and min_width <= 0:
+ break
+ min_width -= len(sep)
+ else:
+ l = max(len(digits), min_width, 1)
+ groups.append('0'*(l - len(digits)) + digits[-l:])
+ return sep.join(reversed(groups))
+
+def _format_sign(is_negative, spec):
+ """Determine sign character."""
+
+ if is_negative:
+ return '-'
+ elif spec['sign'] in ' +':
+ return spec['sign']
+ else:
+ return ''
+
+def _format_number(is_negative, intpart, fracpart, exp, spec):
+ """Format a number, given the following data:
+
+ is_negative: true if the number is negative, else false
+ intpart: string of digits that must appear before the decimal point
+ fracpart: string of digits that must come after the point
+ exp: exponent, as an integer
+ spec: dictionary resulting from parsing the format specifier
+
+ This function uses the information in spec to:
+ insert separators (decimal separator and thousands separators)
+ format the sign
+ format the exponent
+ add trailing '%' for the '%' type
+ zero-pad if necessary
+ fill and align if necessary
+ """
+
+ sign = _format_sign(is_negative, spec)
+
+ if fracpart:
+ fracpart = spec['decimal_point'] + fracpart
+
+ if exp != 0 or spec['type'] in 'eE':
+ echar = {'E': 'E', 'e': 'e', 'G': 'E', 'g': 'e'}[spec['type']]
+ fracpart += "{0}{1:+}".format(echar, exp)
+ if spec['type'] == '%':
+ fracpart += '%'
+
+ if spec['zeropad']:
+ min_width = spec['minimumwidth'] - len(fracpart) - len(sign)
+ else:
+ min_width = 0
+ intpart = _insert_thousands_sep(intpart, spec, min_width)
+
+ return _format_align(sign, intpart+fracpart, spec)
+
+
##### Useful Constants (internal use only) ################################
# Reusable defaults