summaryrefslogtreecommitdiffstats
path: root/Lib/decimal.py
diff options
context:
space:
mode:
Diffstat (limited to 'Lib/decimal.py')
-rw-r--r--Lib/decimal.py126
1 files changed, 83 insertions, 43 deletions
diff --git a/Lib/decimal.py b/Lib/decimal.py
index 727aee2..6480880 100644
--- a/Lib/decimal.py
+++ b/Lib/decimal.py
@@ -1655,47 +1655,53 @@ class Decimal(object):
exp_min = len(self._int) + self._exp - context.prec
if exp_min > Etop:
# overflow: exp_min > Etop iff self.adjusted() > Emax
+ ans = context._raise_error(Overflow, 'above Emax', self._sign)
context._raise_error(Inexact)
context._raise_error(Rounded)
- return context._raise_error(Overflow, 'above Emax', self._sign)
+ return ans
+
self_is_subnormal = exp_min < Etiny
if self_is_subnormal:
- context._raise_error(Subnormal)
exp_min = Etiny
# round if self has too many digits
if self._exp < exp_min:
- context._raise_error(Rounded)
digits = len(self._int) + self._exp - exp_min
if digits < 0:
self = _dec_from_triple(self._sign, '1', exp_min-1)
digits = 0
- this_function = getattr(self, self._pick_rounding_function[context.rounding])
- changed = this_function(digits)
+ rounding_method = self._pick_rounding_function[context.rounding]
+ changed = getattr(self, rounding_method)(digits)
coeff = self._int[:digits] or '0'
- if changed == 1:
+ if changed > 0:
coeff = str(int(coeff)+1)
- ans = _dec_from_triple(self._sign, coeff, exp_min)
+ if len(coeff) > context.prec:
+ coeff = coeff[:-1]
+ exp_min += 1
+ # check whether the rounding pushed the exponent out of range
+ if exp_min > Etop:
+ ans = context._raise_error(Overflow, 'above Emax', self._sign)
+ else:
+ ans = _dec_from_triple(self._sign, coeff, exp_min)
+
+ # raise the appropriate signals, taking care to respect
+ # the precedence described in the specification
+ if changed and self_is_subnormal:
+ context._raise_error(Underflow)
+ if self_is_subnormal:
+ context._raise_error(Subnormal)
if changed:
context._raise_error(Inexact)
- if self_is_subnormal:
- context._raise_error(Underflow)
- if not ans:
- # raise Clamped on underflow to 0
- context._raise_error(Clamped)
- elif len(ans._int) == context.prec+1:
- # we get here only if rescaling rounds the
- # cofficient up to exactly 10**context.prec
- if ans._exp < Etop:
- ans = _dec_from_triple(ans._sign,
- ans._int[:-1], ans._exp+1)
- else:
- # Inexact and Rounded have already been raised
- ans = context._raise_error(Overflow, 'above Emax',
- self._sign)
+ context._raise_error(Rounded)
+ if not ans:
+ # raise Clamped on underflow to 0
+ context._raise_error(Clamped)
return ans
+ if self_is_subnormal:
+ context._raise_error(Subnormal)
+
# fold down if _clamp == 1 and self has too few digits
if context._clamp == 1 and self._exp > Etop:
context._raise_error(Clamped)
@@ -2322,6 +2328,7 @@ class Decimal(object):
# from here on, the result always goes through the call
# to _fix at the end of this function.
ans = None
+ exact = False
# crude test to catch cases of extreme overflow/underflow. If
# log10(self)*other >= 10**bound and bound >= len(str(Emax))
@@ -2346,6 +2353,7 @@ class Decimal(object):
ans = self._power_exact(other, context.prec + 1)
if ans is not None and result_sign == 1:
ans = _dec_from_triple(1, ans._int, ans._exp)
+ exact = True
# usual case: inexact result, x**y computed directly as exp(y*log(x))
if ans is None:
@@ -2368,24 +2376,55 @@ class Decimal(object):
ans = _dec_from_triple(result_sign, str(coeff), exp)
- # the specification says that for non-integer other we need to
- # raise Inexact, even when the result is actually exact. In
- # the same way, we need to raise Underflow here if the result
- # is subnormal. (The call to _fix will take care of raising
- # Rounded and Subnormal, as usual.)
- if not other._isinteger():
- context._raise_error(Inexact)
- # pad with zeros up to length context.prec+1 if necessary
+ # unlike exp, ln and log10, the power function respects the
+ # rounding mode; no need to switch to ROUND_HALF_EVEN here
+
+ # There's a difficulty here when 'other' is not an integer and
+ # the result is exact. In this case, the specification
+ # requires that the Inexact flag be raised (in spite of
+ # exactness), but since the result is exact _fix won't do this
+ # for us. (Correspondingly, the Underflow signal should also
+ # be raised for subnormal results.) We can't directly raise
+ # these signals either before or after calling _fix, since
+ # that would violate the precedence for signals. So we wrap
+ # the ._fix call in a temporary context, and reraise
+ # afterwards.
+ if exact and not other._isinteger():
+ # pad with zeros up to length context.prec+1 if necessary; this
+ # ensures that the Rounded signal will be raised.
if len(ans._int) <= context.prec:
- expdiff = context.prec+1 - len(ans._int)
+ expdiff = context.prec + 1 - len(ans._int)
ans = _dec_from_triple(ans._sign, ans._int+'0'*expdiff,
ans._exp-expdiff)
- if ans.adjusted() < context.Emin:
- context._raise_error(Underflow)
- # unlike exp, ln and log10, the power function respects the
- # rounding mode; no need to use ROUND_HALF_EVEN here
- ans = ans._fix(context)
+ # create a copy of the current context, with cleared flags/traps
+ newcontext = context.copy()
+ newcontext.clear_flags()
+ for exception in _signals:
+ newcontext.traps[exception] = 0
+
+ # round in the new context
+ ans = ans._fix(newcontext)
+
+ # raise Inexact, and if necessary, Underflow
+ newcontext._raise_error(Inexact)
+ if newcontext.flags[Subnormal]:
+ newcontext._raise_error(Underflow)
+
+ # propagate signals to the original context; _fix could
+ # have raised any of Overflow, Underflow, Subnormal,
+ # Inexact, Rounded, Clamped. Overflow needs the correct
+ # arguments. Note that the order of the exceptions is
+ # important here.
+ if newcontext.flags[Overflow]:
+ context._raise_error(Overflow, 'above Emax', ans._sign)
+ for exception in Underflow, Subnormal, Inexact, Rounded, Clamped:
+ if newcontext.flags[exception]:
+ context._raise_error(exception)
+
+ else:
+ ans = ans._fix(context)
+
return ans
def __rpow__(self, other, context=None):
@@ -2479,14 +2518,15 @@ class Decimal(object):
'quantize result has too many digits for current context')
# raise appropriate flags
+ if ans and ans.adjusted() < context.Emin:
+ context._raise_error(Subnormal)
if ans._exp > self._exp:
- context._raise_error(Rounded)
if ans != self:
context._raise_error(Inexact)
- if ans and ans.adjusted() < context.Emin:
- context._raise_error(Subnormal)
+ context._raise_error(Rounded)
- # call to fix takes care of any necessary folddown
+ # call to fix takes care of any necessary folddown, and
+ # signals Clamped if necessary
ans = ans._fix(context)
return ans
@@ -2585,10 +2625,10 @@ class Decimal(object):
context = getcontext()
if rounding is None:
rounding = context.rounding
- context._raise_error(Rounded)
ans = self._rescale(0, rounding)
if ans != self:
context._raise_error(Inexact)
+ context._raise_error(Rounded)
return ans
def to_integral_value(self, rounding=None, context=None):
@@ -3469,13 +3509,13 @@ class Decimal(object):
context._raise_error(Overflow,
'Infinite result from next_toward',
ans._sign)
- context._raise_error(Rounded)
context._raise_error(Inexact)
+ context._raise_error(Rounded)
elif ans.adjusted() < context.Emin:
context._raise_error(Underflow)
context._raise_error(Subnormal)
- context._raise_error(Rounded)
context._raise_error(Inexact)
+ context._raise_error(Rounded)
# if precision == 1 then we don't raise Clamped for a
# result 0E-Etiny.
if not ans: