summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFacundo Batista <facundobatista@gmail.com>2007-12-03 17:55:00 (GMT)
committerFacundo Batista <facundobatista@gmail.com>2007-12-03 17:55:00 (GMT)
commit2ec7415db5ad63c4e4b27ee793214071454f6fe5 (patch)
tree18f0e65379a300110f9f16a92dd2eb41febf8d21
parent62edb71556d26f924a0e2e949af9cd3c211dea23 (diff)
downloadcpython-2ec7415db5ad63c4e4b27ee793214071454f6fe5.zip
cpython-2ec7415db5ad63c4e4b27ee793214071454f6fe5.tar.gz
cpython-2ec7415db5ad63c4e4b27ee793214071454f6fe5.tar.bz2
Faster _fix function, and some reordering for a more elegant
coding. Thanks Mark Dickinson.
-rw-r--r--Lib/decimal.py99
1 files changed, 47 insertions, 52 deletions
diff --git a/Lib/decimal.py b/Lib/decimal.py
index c503d2f..0ce9a34 100644
--- a/Lib/decimal.py
+++ b/Lib/decimal.py
@@ -1077,29 +1077,6 @@ class Decimal(object):
return other.__sub__(self, context=context)
- def _increment(self):
- """Special case of add, adding 1eExponent
-
- Since it is common, (rounding, for example) this adds
- (sign)*one E self._exp to the number more efficiently than add.
-
- Assumes that self is nonspecial.
-
- For example:
- Decimal('5.624e10')._increment() == Decimal('5.625e10')
- """
- L = map(int, self._int)
- L[-1] += 1
- spot = len(L)-1
- while L[spot] == 10:
- L[spot] = 0
- if spot == 0:
- L[0:0] = [1]
- break
- L[spot-1] += 1
- spot -= 1
- return _dec_from_triple(self._sign, "".join(map(str, L)), self._exp)
-
def __mul__(self, other, context=None):
"""Return self * other.
@@ -1540,8 +1517,18 @@ class Decimal(object):
# round if self has too many digits
if self._exp < exp_min:
context._raise_error(Rounded)
- ans = self._rescale(exp_min, context.rounding)
- if ans != self:
+ 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)
+ coeff = self._int[:digits] or '0'
+ if changed == 1:
+ coeff = str(int(coeff)+1)
+ ans = _dec_from_triple(self._sign, coeff, exp_min)
+
+ if changed:
context._raise_error(Inexact)
if self_is_subnormal:
context._raise_error(Underflow)
@@ -1574,66 +1561,68 @@ class Decimal(object):
# for each of the rounding functions below:
# self is a finite, nonzero Decimal
# prec is an integer satisfying 0 <= prec < len(self._int)
- # the rounded result will have exponent self._exp + len(self._int) - prec;
+ #
+ # each function returns either -1, 0, or 1, as follows:
+ # 1 indicates that self should be rounded up (away from zero)
+ # 0 indicates that self should be truncated, and that all the
+ # digits to be truncated are zeros (so the value is unchanged)
+ # -1 indicates that there are nonzero digits to be truncated
def _round_down(self, prec):
"""Also known as round-towards-0, truncate."""
- newexp = self._exp + len(self._int) - prec
- return _dec_from_triple(self._sign, self._int[:prec] or '0', newexp)
+ if _all_zeros(self._int, prec):
+ return 0
+ else:
+ return -1
def _round_up(self, prec):
"""Rounds away from 0."""
- newexp = self._exp + len(self._int) - prec
- tmp = _dec_from_triple(self._sign, self._int[:prec] or '0', newexp)
- for digit in self._int[prec:]:
- if digit != '0':
- return tmp._increment()
- return tmp
+ return -self._round_down(prec)
def _round_half_up(self, prec):
"""Rounds 5 up (away from 0)"""
if self._int[prec] in '56789':
- return self._round_up(prec)
+ return 1
+ elif _all_zeros(self._int, prec):
+ return 0
else:
- return self._round_down(prec)
+ return -1
def _round_half_down(self, prec):
"""Round 5 down"""
- if self._int[prec] == '5':
- for digit in self._int[prec+1:]:
- if digit != '0':
- break
- else:
- return self._round_down(prec)
- return self._round_half_up(prec)
+ if _exact_half(self._int, prec):
+ return -1
+ else:
+ return self._round_half_up(prec)
def _round_half_even(self, prec):
"""Round 5 to even, rest to nearest."""
- if prec and self._int[prec-1] in '13579':
- return self._round_half_up(prec)
+ if _exact_half(self._int, prec) and \
+ (prec == 0 or self._int[prec-1] in '02468'):
+ return -1
else:
- return self._round_half_down(prec)
+ return self._round_half_up(prec)
def _round_ceiling(self, prec):
"""Rounds up (not away from 0 if negative.)"""
if self._sign:
return self._round_down(prec)
else:
- return self._round_up(prec)
+ return -self._round_down(prec)
def _round_floor(self, prec):
"""Rounds down (not towards 0 if negative)"""
if not self._sign:
return self._round_down(prec)
else:
- return self._round_up(prec)
+ return -self._round_down(prec)
def _round_05up(self, prec):
"""Round down unless digit prec-1 is 0 or 5."""
- if prec == 0 or self._int[prec-1] in '05':
- return self._round_up(prec)
- else:
+ if prec and self._int[prec-1] not in '05':
return self._round_down(prec)
+ else:
+ return -self._round_down(prec)
def fma(self, other, third, context=None):
"""Fused multiply-add.
@@ -2290,7 +2279,11 @@ class Decimal(object):
self = _dec_from_triple(self._sign, '1', exp-1)
digits = 0
this_function = getattr(self, self._pick_rounding_function[rounding])
- return this_function(digits)
+ changed = this_function(digits)
+ coeff = self._int[:digits] or '0'
+ if changed == 1:
+ coeff = str(int(coeff)+1)
+ return _dec_from_triple(self._sign, coeff, exp)
def to_integral_exact(self, rounding=None, context=None):
"""Rounds to a nearby integer.
@@ -5198,6 +5191,8 @@ _parser = re.compile(r""" # A numeric string consists of:
$
""", re.VERBOSE | re.IGNORECASE).match
+_all_zeros = re.compile('0*$').match
+_exact_half = re.compile('50*$').match
del re