mirror of https://github.com/python/cpython.git
Speed up of the various division operations (remainder, divide,
divideint and divmod). Thanks Mark Dickinson.
This commit is contained in:
parent
745e48dffa
commit
cce8df2f67
288
Lib/decimal.py
288
Lib/decimal.py
|
@ -244,9 +244,7 @@ class DivisionByZero(DecimalException, ZeroDivisionError):
|
||||||
-0, for power.
|
-0, for power.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def handle(self, context, sign, double = None, *args):
|
def handle(self, context, sign, *args):
|
||||||
if double is not None:
|
|
||||||
return (Infsign[sign],)*2
|
|
||||||
return Infsign[sign]
|
return Infsign[sign]
|
||||||
|
|
||||||
class DivisionImpossible(InvalidOperation):
|
class DivisionImpossible(InvalidOperation):
|
||||||
|
@ -258,7 +256,7 @@ class DivisionImpossible(InvalidOperation):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def handle(self, context, *args):
|
def handle(self, context, *args):
|
||||||
return (NaN, NaN)
|
return NaN
|
||||||
|
|
||||||
class DivisionUndefined(InvalidOperation, ZeroDivisionError):
|
class DivisionUndefined(InvalidOperation, ZeroDivisionError):
|
||||||
"""Undefined result of division.
|
"""Undefined result of division.
|
||||||
|
@ -268,9 +266,7 @@ class DivisionUndefined(InvalidOperation, ZeroDivisionError):
|
||||||
the dividend is also zero. The result is [0,qNaN].
|
the dividend is also zero. The result is [0,qNaN].
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def handle(self, context, tup=None, *args):
|
def handle(self, context, *args):
|
||||||
if tup is not None:
|
|
||||||
return (NaN, NaN) # for 0 %0, 0 // 0
|
|
||||||
return NaN
|
return NaN
|
||||||
|
|
||||||
class Inexact(DecimalException):
|
class Inexact(DecimalException):
|
||||||
|
@ -1151,157 +1147,97 @@ def __mul__(self, other, context=None):
|
||||||
|
|
||||||
def __div__(self, other, context=None):
|
def __div__(self, other, context=None):
|
||||||
"""Return self / other."""
|
"""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)
|
other = _convert_other(other)
|
||||||
if other is NotImplemented:
|
if other is NotImplemented:
|
||||||
if divmod in (0, 1):
|
|
||||||
return NotImplemented
|
return NotImplemented
|
||||||
return (NotImplemented, NotImplemented)
|
|
||||||
|
|
||||||
if context is None:
|
if context is None:
|
||||||
context = getcontext()
|
context = getcontext()
|
||||||
|
|
||||||
shouldround = context._rounding_decision == ALWAYS_ROUND
|
|
||||||
|
|
||||||
sign = self._sign ^ other._sign
|
sign = self._sign ^ other._sign
|
||||||
|
|
||||||
if self._is_special or other._is_special:
|
if self._is_special or other._is_special:
|
||||||
ans = self._check_nans(other, context)
|
ans = self._check_nans(other, context)
|
||||||
if ans:
|
if ans:
|
||||||
if divmod:
|
|
||||||
return (ans, ans)
|
|
||||||
return ans
|
return ans
|
||||||
|
|
||||||
if self._isinfinity() and other._isinfinity():
|
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')
|
return context._raise_error(InvalidOperation, '(+-)INF/(+-)INF')
|
||||||
|
|
||||||
if self._isinfinity():
|
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]
|
return Infsign[sign]
|
||||||
|
|
||||||
if other._isinfinity():
|
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')
|
context._raise_error(Clamped, 'Division by infinity')
|
||||||
return Decimal((sign, (0,), context.Etiny()))
|
return Decimal((sign, (0,), context.Etiny()))
|
||||||
|
|
||||||
# Special cases for zeroes
|
# 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 not other:
|
||||||
if divmod:
|
if not self:
|
||||||
return context._raise_error(DivisionByZero, 'divmod(x,0)',
|
return context._raise_error(DivisionUndefined, '0 / 0')
|
||||||
sign, 1)
|
|
||||||
return context._raise_error(DivisionByZero, 'x / 0', sign)
|
return context._raise_error(DivisionByZero, 'x / 0', sign)
|
||||||
|
|
||||||
|
if not self:
|
||||||
|
exp = self._exp - other._exp
|
||||||
|
coeff = 0
|
||||||
|
else:
|
||||||
# OK, so neither = 0, INF or NaN
|
# OK, so neither = 0, INF or NaN
|
||||||
|
shift = len(other._int) - len(self._int) + context.prec + 1
|
||||||
# If we're dividing into ints, and self < other, stop.
|
exp = self._exp - other._exp - shift
|
||||||
# self.__abs__(0) does not round.
|
|
||||||
if divmod and (self.__abs__(0, context) < other.__abs__(0, context)):
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
elif divmod == 2:
|
|
||||||
# Don't round the mod part, if we don't need it.
|
|
||||||
return (Decimal( (sign, (0,), 0) ), Decimal(self))
|
|
||||||
|
|
||||||
op1 = _WorkRep(self)
|
op1 = _WorkRep(self)
|
||||||
op2 = _WorkRep(other)
|
op2 = _WorkRep(other)
|
||||||
op1, op2, adjust = _adjust_coefficients(op1, op2)
|
if shift >= 0:
|
||||||
res = _WorkRep( (sign, 0, (op1.exp - op2.exp)) )
|
coeff, remainder = divmod(op1.int * 10**shift, op2.int)
|
||||||
if divmod and res.exp > context.prec + 1:
|
else:
|
||||||
return context._raise_error(DivisionImpossible)
|
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
|
||||||
|
|
||||||
prec_limit = 10 ** context.prec
|
ans = Decimal((sign, map(int, str(coeff)), exp))
|
||||||
while 1:
|
return ans._fix(context)
|
||||||
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:
|
__truediv__ = __div__
|
||||||
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
|
|
||||||
|
|
||||||
break
|
def _divide(self, other, context):
|
||||||
res.int *= 10
|
"""Return (self // other, self % other), to context.prec precision.
|
||||||
res.exp -= 1
|
|
||||||
adjust += 1
|
|
||||||
op1.int *= 10
|
|
||||||
op1.exp -= 1
|
|
||||||
|
|
||||||
ans = Decimal(res)
|
Assumes that neither self nor other is a NaN, that self is not
|
||||||
if shouldround:
|
infinite and that other is nonzero.
|
||||||
ans = ans._fix(context)
|
"""
|
||||||
return ans
|
sign = self._sign ^ other._sign
|
||||||
|
if other._isinfinity():
|
||||||
|
ideal_exp = self._exp
|
||||||
|
else:
|
||||||
|
ideal_exp = min(self._exp, other._exp)
|
||||||
|
|
||||||
|
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)))
|
||||||
|
|
||||||
|
# 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):
|
def __rdiv__(self, other, context=None):
|
||||||
"""Swaps self/other and returns __div__."""
|
"""Swaps self/other and returns __div__."""
|
||||||
|
@ -1313,9 +1249,40 @@ def __rdiv__(self, other, context=None):
|
||||||
|
|
||||||
def __divmod__(self, other, context=None):
|
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):
|
def __rdivmod__(self, other, context=None):
|
||||||
"""Swaps self/other and returns __divmod__."""
|
"""Swaps self/other and returns __divmod__."""
|
||||||
|
@ -1332,15 +1299,25 @@ def __mod__(self, other, context=None):
|
||||||
if other is NotImplemented:
|
if other is NotImplemented:
|
||||||
return other
|
return other
|
||||||
|
|
||||||
if self._is_special or other._is_special:
|
if context is None:
|
||||||
|
context = getcontext()
|
||||||
|
|
||||||
ans = self._check_nans(other, context)
|
ans = self._check_nans(other, context)
|
||||||
if ans:
|
if ans:
|
||||||
return ans
|
return ans
|
||||||
|
|
||||||
if self and not other:
|
if self._isinfinity():
|
||||||
|
return context._raise_error(InvalidOperation, 'INF % x')
|
||||||
|
elif not other:
|
||||||
|
if self:
|
||||||
return context._raise_error(InvalidOperation, 'x % 0')
|
return context._raise_error(InvalidOperation, 'x % 0')
|
||||||
|
else:
|
||||||
|
return context._raise_error(DivisionUndefined, '0 % 0')
|
||||||
|
|
||||||
return self._divide(other, 3, context)[1]
|
remainder = self._divide(other, context)[1]
|
||||||
|
if context._rounding_decision == ALWAYS_ROUND:
|
||||||
|
remainder = remainder._fix(context)
|
||||||
|
return remainder
|
||||||
|
|
||||||
def __rmod__(self, other, context=None):
|
def __rmod__(self, other, context=None):
|
||||||
"""Swaps self/other and returns __mod__."""
|
"""Swaps self/other and returns __mod__."""
|
||||||
|
@ -1391,7 +1368,7 @@ def remainder_near(self, other, context=None):
|
||||||
expdiff = self.adjusted() - other.adjusted()
|
expdiff = self.adjusted() - other.adjusted()
|
||||||
if expdiff >= context.prec + 1:
|
if expdiff >= context.prec + 1:
|
||||||
# expdiff >= prec+1 => abs(self/other) > 10**prec
|
# expdiff >= prec+1 => abs(self/other) > 10**prec
|
||||||
return context._raise_error(DivisionImpossible)[0]
|
return context._raise_error(DivisionImpossible)
|
||||||
if expdiff <= -2:
|
if expdiff <= -2:
|
||||||
# expdiff <= -2 => abs(self/other) < 0.1
|
# expdiff <= -2 => abs(self/other) < 0.1
|
||||||
ans = self._rescale(ideal_exponent, context.rounding)
|
ans = self._rescale(ideal_exponent, context.rounding)
|
||||||
|
@ -1413,7 +1390,7 @@ def remainder_near(self, other, context=None):
|
||||||
q += 1
|
q += 1
|
||||||
|
|
||||||
if q >= 10**context.prec:
|
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
|
# result has same sign as self unless r is negative
|
||||||
sign = self._sign
|
sign = self._sign
|
||||||
|
@ -1426,7 +1403,31 @@ def remainder_near(self, other, context=None):
|
||||||
|
|
||||||
def __floordiv__(self, other, context=None):
|
def __floordiv__(self, other, context=None):
|
||||||
"""self // other"""
|
"""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):
|
def __rfloordiv__(self, other, context=None):
|
||||||
"""Swaps self/other and returns __floordiv__."""
|
"""Swaps self/other and returns __floordiv__."""
|
||||||
|
@ -2979,7 +2980,7 @@ def logb(self, context=None):
|
||||||
|
|
||||||
# logb(0) = -Inf, DivisionByZero
|
# logb(0) = -Inf, DivisionByZero
|
||||||
if not self:
|
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
|
# otherwise, simply return the adjusted exponent of self, as a
|
||||||
# Decimal. Note that no attempt is made to fit the result
|
# 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
|
tmp.exp = other.exp
|
||||||
return op1, op2
|
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__ #####
|
##### Integer arithmetic functions used by ln, log10, exp and __pow__ #####
|
||||||
|
|
||||||
# This function from Tim Peters was taken from here:
|
# This function from Tim Peters was taken from here:
|
||||||
|
|
Loading…
Reference in New Issue