summaryrefslogtreecommitdiffstats
path: root/Lib/decimal.py
diff options
context:
space:
mode:
authorFacundo Batista <facundobatista@gmail.com>2007-09-18 16:53:18 (GMT)
committerFacundo Batista <facundobatista@gmail.com>2007-09-18 16:53:18 (GMT)
commitcce8df2f678b1e1ec11c86cf2fabf12c2077577d (patch)
tree0b91c1a546500edcba8a9f3e814ecc6e297af079 /Lib/decimal.py
parent745e48dffa89418b547e0eed3c8697c72826a2a3 (diff)
downloadcpython-cce8df2f678b1e1ec11c86cf2fabf12c2077577d.zip
cpython-cce8df2f678b1e1ec11c86cf2fabf12c2077577d.tar.gz
cpython-cce8df2f678b1e1ec11c86cf2fabf12c2077577d.tar.bz2
Speed up of the various division operations (remainder, divide,
divideint and divmod). Thanks Mark Dickinson.
Diffstat (limited to 'Lib/decimal.py')
-rw-r--r--Lib/decimal.py300
1 files changed, 139 insertions, 161 deletions
diff --git a/Lib/decimal.py b/Lib/decimal.py
index 6481d6c..4137d06 100644
--- a/Lib/decimal.py
+++ b/Lib/decimal.py
@@ -244,9 +244,7 @@ class DivisionByZero(DecimalException, ZeroDivisionError):
-0, for power.
"""
- def handle(self, context, sign, double = None, *args):
- if double is not None:
- return (Infsign[sign],)*2
+ def handle(self, context, sign, *args):
return Infsign[sign]
class DivisionImpossible(InvalidOperation):
@@ -258,7 +256,7 @@ class DivisionImpossible(InvalidOperation):
"""
def handle(self, context, *args):
- return (NaN, NaN)
+ return NaN
class DivisionUndefined(InvalidOperation, ZeroDivisionError):
"""Undefined result of division.
@@ -268,9 +266,7 @@ class DivisionUndefined(InvalidOperation, ZeroDivisionError):
the dividend is also zero. The result is [0,qNaN].
"""
- def handle(self, context, tup=None, *args):
- if tup is not None:
- return (NaN, NaN) # for 0 %0, 0 // 0
+ def handle(self, context, *args):
return NaN
class Inexact(DecimalException):
@@ -1151,157 +1147,97 @@ class Decimal(object):
def __div__(self, other, context=None):
"""Return self / other."""
- return self._divide(other, context=context)
- __truediv__ = __div__
-
- def _divide(self, other, divmod = 0, context=None):
- """Return a / b, to context.prec precision.
-
- divmod:
- 0 => true division
- 1 => (a //b, a%b)
- 2 => a //b
- 3 => a%b
-
- Actually, if divmod is 2 or 3 a tuple is returned, but errors for
- computing the other value are not raised.
- """
other = _convert_other(other)
if other is NotImplemented:
- if divmod in (0, 1):
- return NotImplemented
- return (NotImplemented, NotImplemented)
+ return NotImplemented
if context is None:
context = getcontext()
- shouldround = context._rounding_decision == ALWAYS_ROUND
-
sign = self._sign ^ other._sign
if self._is_special or other._is_special:
ans = self._check_nans(other, context)
if ans:
- if divmod:
- return (ans, ans)
return ans
if self._isinfinity() and other._isinfinity():
- if divmod:
- reloco = (context._raise_error(InvalidOperation,
- '(+-)INF // (+-)INF'),
- context._raise_error(InvalidOperation,
- '(+-)INF % (+-)INF'))
- return reloco
return context._raise_error(InvalidOperation, '(+-)INF/(+-)INF')
if self._isinfinity():
- if divmod == 1:
- return (Infsign[sign],
- context._raise_error(InvalidOperation, 'INF % x'))
- elif divmod == 2:
- return (Infsign[sign], NaN)
- elif divmod == 3:
- return (Infsign[sign],
- context._raise_error(InvalidOperation, 'INF % x'))
return Infsign[sign]
if other._isinfinity():
- if divmod:
- otherside = Decimal(self)
- if shouldround and (divmod == 1 or divmod == 3):
- otherside = otherside._fix(context)
- return (Decimal((sign, (0,), 0)), otherside)
context._raise_error(Clamped, 'Division by infinity')
return Decimal((sign, (0,), context.Etiny()))
# Special cases for zeroes
- if not self and not other:
- if divmod:
- return context._raise_error(DivisionUndefined, '0 / 0', 1)
- return context._raise_error(DivisionUndefined, '0 / 0')
-
- if not self:
- if divmod:
- otherside = Decimal((self._sign, (0,), min(self._exp, other._exp)))
- if shouldround and (divmod == 1 or divmod == 3):
- otherside = otherside._fix(context)
- return (Decimal((sign, (0,), 0)), otherside)
- exp = self._exp - other._exp
- ans = Decimal((sign, (0,), exp))
- ans = ans._fix(context)
- return ans
-
if not other:
- if divmod:
- return context._raise_error(DivisionByZero, 'divmod(x,0)',
- sign, 1)
+ if not self:
+ return context._raise_error(DivisionUndefined, '0 / 0')
return context._raise_error(DivisionByZero, 'x / 0', sign)
- # OK, so neither = 0, INF or NaN
-
- # If we're dividing into ints, and self < other, stop.
- # self.__abs__(0) does not round.
- if divmod and (self.__abs__(0, context) < other.__abs__(0, context)):
+ if not self:
+ exp = self._exp - other._exp
+ coeff = 0
+ else:
+ # OK, so neither = 0, INF or NaN
+ shift = len(other._int) - len(self._int) + context.prec + 1
+ exp = self._exp - other._exp - shift
+ op1 = _WorkRep(self)
+ op2 = _WorkRep(other)
+ if shift >= 0:
+ coeff, remainder = divmod(op1.int * 10**shift, op2.int)
+ else:
+ coeff, remainder = divmod(op1.int, op2.int * 10**-shift)
+ if remainder:
+ # result is not exact; adjust to ensure correct rounding
+ if coeff % 5 == 0:
+ coeff += 1
+ else:
+ # result is exact; get as close to ideal exponent as possible
+ ideal_exp = self._exp - other._exp
+ while exp < ideal_exp and coeff % 10 == 0:
+ coeff //= 10
+ exp += 1
- if divmod == 1 or divmod == 3:
- exp = min(self._exp, other._exp)
- ans2 = self._rescale(exp, context.rounding)
- if shouldround:
- ans2 = ans2._fix(context)
- return (Decimal( (sign, (0,), 0) ),
- ans2)
+ ans = Decimal((sign, map(int, str(coeff)), exp))
+ return ans._fix(context)
- elif divmod == 2:
- # Don't round the mod part, if we don't need it.
- return (Decimal( (sign, (0,), 0) ), Decimal(self))
+ __truediv__ = __div__
- op1 = _WorkRep(self)
- op2 = _WorkRep(other)
- op1, op2, adjust = _adjust_coefficients(op1, op2)
- res = _WorkRep( (sign, 0, (op1.exp - op2.exp)) )
- if divmod and res.exp > context.prec + 1:
- return context._raise_error(DivisionImpossible)
+ def _divide(self, other, context):
+ """Return (self // other, self % other), to context.prec precision.
- prec_limit = 10 ** context.prec
- while 1:
- while op2.int <= op1.int:
- res.int += 1
- op1.int -= op2.int
- if res.exp == 0 and divmod:
- if res.int >= prec_limit and shouldround:
- return context._raise_error(DivisionImpossible)
- otherside = Decimal(op1)
- exp = min(self._exp, other._exp)
- otherside = otherside._rescale(exp, context.rounding)
- if shouldround and (divmod == 1 or divmod == 3):
- otherside = otherside._fix(context)
- return (Decimal(res), otherside)
-
- if op1.int == 0 and adjust >= 0 and not divmod:
- break
- if res.int >= prec_limit and shouldround:
- if divmod:
- return context._raise_error(DivisionImpossible)
- shouldround=1
- # Really, the answer is a bit higher, so adding a one to
- # the end will make sure the rounding is right.
- if op1.int != 0:
- res.int *= 10
- res.int += 1
- res.exp -= 1
+ Assumes that neither self nor other is a NaN, that self is not
+ infinite and that other is nonzero.
+ """
+ sign = self._sign ^ other._sign
+ if other._isinfinity():
+ ideal_exp = self._exp
+ else:
+ ideal_exp = min(self._exp, other._exp)
- break
- res.int *= 10
- res.exp -= 1
- adjust += 1
- op1.int *= 10
- op1.exp -= 1
+ expdiff = self.adjusted() - other.adjusted()
+ if not self or other._isinfinity() or expdiff <= -2:
+ return (Decimal((sign, (0,), 0)),
+ self._rescale(ideal_exp, context.rounding))
+ if expdiff <= context.prec:
+ op1 = _WorkRep(self)
+ op2 = _WorkRep(other)
+ if op1.exp >= op2.exp:
+ op1.int *= 10**(op1.exp - op2.exp)
+ else:
+ op2.int *= 10**(op2.exp - op1.exp)
+ q, r = divmod(op1.int, op2.int)
+ if q < 10**context.prec:
+ return (Decimal((sign, map(int, str(q)), 0)),
+ Decimal((self._sign, map(int, str(r)), ideal_exp)))
- ans = Decimal(res)
- if shouldround:
- ans = ans._fix(context)
- return ans
+ # Here the quotient is too large to be representable
+ ans = context._raise_error(DivisionImpossible,
+ 'quotient too large in //, % or divmod')
+ return ans, ans
def __rdiv__(self, other, context=None):
"""Swaps self/other and returns __div__."""
@@ -1313,9 +1249,40 @@ class Decimal(object):
def __divmod__(self, other, context=None):
"""
- (self // other, self % other)
+ Return (self // other, self % other)
"""
- return self._divide(other, 1, context)
+ other = _convert_other(other)
+ if other is NotImplemented:
+ return other
+
+ if context is None:
+ context = getcontext()
+
+ ans = self._check_nans(other, context)
+ if ans:
+ return (ans, ans)
+
+ sign = self._sign ^ other._sign
+ if self._isinfinity():
+ if other._isinfinity():
+ ans = context._raise_error(InvalidOperation, 'divmod(INF, INF)')
+ return ans, ans
+ else:
+ return (Infsign[sign],
+ context._raise_error(InvalidOperation, 'INF % x'))
+
+ if not other:
+ if not self:
+ ans = context._raise_error(DivisionUndefined, 'divmod(0, 0)')
+ return ans, ans
+ else:
+ return (context._raise_error(DivisionByZero, 'x // 0', sign),
+ context._raise_error(InvalidOperation, 'x % 0'))
+
+ quotient, remainder = self._divide(other, context)
+ if context._rounding_decision == ALWAYS_ROUND:
+ remainder = remainder._fix(context)
+ return quotient, remainder
def __rdivmod__(self, other, context=None):
"""Swaps self/other and returns __divmod__."""
@@ -1332,15 +1299,25 @@ class Decimal(object):
if other is NotImplemented:
return other
- if self._is_special or other._is_special:
- ans = self._check_nans(other, context)
- if ans:
- return ans
+ if context is None:
+ context = getcontext()
- if self and not other:
- return context._raise_error(InvalidOperation, 'x % 0')
+ ans = self._check_nans(other, context)
+ if ans:
+ return ans
- return self._divide(other, 3, context)[1]
+ if self._isinfinity():
+ return context._raise_error(InvalidOperation, 'INF % x')
+ elif not other:
+ if self:
+ return context._raise_error(InvalidOperation, 'x % 0')
+ else:
+ return context._raise_error(DivisionUndefined, '0 % 0')
+
+ remainder = self._divide(other, context)[1]
+ if context._rounding_decision == ALWAYS_ROUND:
+ remainder = remainder._fix(context)
+ return remainder
def __rmod__(self, other, context=None):
"""Swaps self/other and returns __mod__."""
@@ -1391,7 +1368,7 @@ class Decimal(object):
expdiff = self.adjusted() - other.adjusted()
if expdiff >= context.prec + 1:
# expdiff >= prec+1 => abs(self/other) > 10**prec
- return context._raise_error(DivisionImpossible)[0]
+ return context._raise_error(DivisionImpossible)
if expdiff <= -2:
# expdiff <= -2 => abs(self/other) < 0.1
ans = self._rescale(ideal_exponent, context.rounding)
@@ -1413,7 +1390,7 @@ class Decimal(object):
q += 1
if q >= 10**context.prec:
- return context._raise_error(DivisionImpossible)[0]
+ return context._raise_error(DivisionImpossible)
# result has same sign as self unless r is negative
sign = self._sign
@@ -1426,7 +1403,31 @@ class Decimal(object):
def __floordiv__(self, other, context=None):
"""self // other"""
- return self._divide(other, 2, context)[0]
+ other = _convert_other(other)
+ if other is NotImplemented:
+ return other
+
+ if context is None:
+ context = getcontext()
+
+ ans = self._check_nans(other, context)
+ if ans:
+ return ans
+
+ if self._isinfinity():
+ if other._isinfinity():
+ return context._raise_error(InvalidOperation, 'INF // INF')
+ else:
+ return Infsign[self._sign ^ other._sign]
+
+ if not other:
+ if self:
+ return context._raise_error(DivisionByZero, 'x // 0',
+ self._sign ^ other._sign)
+ else:
+ return context._raise_error(DivisionUndefined, '0 // 0')
+
+ return self._divide(other, context)[0]
def __rfloordiv__(self, other, context=None):
"""Swaps self/other and returns __floordiv__."""
@@ -2979,7 +2980,7 @@ class Decimal(object):
# logb(0) = -Inf, DivisionByZero
if not self:
- return context._raise_error(DivisionByZero, 'logb(0)', -1)
+ return context._raise_error(DivisionByZero, 'logb(0)', 1)
# otherwise, simply return the adjusted exponent of self, as a
# Decimal. Note that no attempt is made to fit the result
@@ -4793,29 +4794,6 @@ def _normalize(op1, op2, shouldround = 0, prec = 0):
tmp.exp = other.exp
return op1, op2
-def _adjust_coefficients(op1, op2):
- """Adjust op1, op2 so that op2.int * 10 > op1.int >= op2.int.
-
- Returns the adjusted op1, op2 as well as the change in op1.exp-op2.exp.
-
- Used on _WorkRep instances during division.
- """
- adjust = 0
- # If op1 is smaller, make it larger
- while op2.int > op1.int:
- op1.int *= 10
- op1.exp -= 1
- adjust += 1
-
- # If op2 is too small, make it larger
- while op1.int >= (10 * op2.int):
- op2.int *= 10
- op2.exp -= 1
- adjust -= 1
-
- return op1, op2, adjust
-
-
##### Integer arithmetic functions used by ln, log10, exp and __pow__ #####
# This function from Tim Peters was taken from here: