[Python-checkins] r57039 - python/branches/decimal-branch/Lib/decimal.py

facundo.batista python-checkins at python.org
Tue Aug 14 22:58:14 CEST 2007


Author: facundo.batista
Date: Tue Aug 14 22:58:13 2007
New Revision: 57039

Modified:
   python/branches/decimal-branch/Lib/decimal.py
Log:

Coded the log10() function, with an auxiliary one specific for it.
Also fixed the doctest to do what the Specification says.  All
tests pass ok. (Thanks Mark Dickinson)


Modified: python/branches/decimal-branch/Lib/decimal.py
==============================================================================
--- python/branches/decimal-branch/Lib/decimal.py	(original)
+++ python/branches/decimal-branch/Lib/decimal.py	Tue Aug 14 22:58:13 2007
@@ -2705,9 +2705,92 @@
         context.rounding = rounding
         return ans
 
+    def _log10_exp_bound(self):
+        """Compute a lower bound for the adjusted exponent of self.log10().
+        In other words, find r such that self.log10() >= 10**r.
+        Assumes that self is finite and positive and that self != 1.
+        """
+
+        # For x >= 10 or x < 0.1 we only need a bound on the integer
+        # part of log10(self), and this comes directly from the
+        # exponent of x.  For 0.1 <= x <= 10 we use the inequalities
+        # 1-1/x <= log(x) <= x-1. If x > 1 we have |log10(x)| >
+        # (1-1/x)/2.31 > 0.  If x < 1 then |log10(x)| > (1-x)/2.31 > 0
+
+        adj = self._exp + len(self._int) - 1
+        if adj >= 1:
+            # self >= 10
+            return len(str(adj))-1
+        if adj <= -2:
+            # self < 0.1
+            return len(str(-1-adj))-1
+        op = _WorkRep(self)
+        c, e = op.int, op.exp
+        if adj == 0:
+            # 1 < self < 10
+            num = str(c-10**-e)
+            den = str(231*c)
+            return len(num) - len(den) - (num < den) + 2
+        # adj == -1, 0.1 <= self < 1
+        num = str(10**-e-c)
+        return len(num) + e - (num < "231") - 1
+
     def log10(self, context=None):
         """Returns the base 10 logarithm of self."""
 
+        if context is None:
+            context = getcontext()
+
+        # check context and operand
+        ans = self._checkMath(context)
+        if ans:
+            return ans
+
+        # log10(NaN) = NaN
+        ans = self._check_nans(context=context)
+        if ans:
+            return ans
+
+        # log10(0.0) == -Infinity
+        if not self:
+            return negInf
+
+        # log10(Infinity) = Infinity
+        if self._isinfinity() == 1:
+            return Inf
+
+        # log10(negative or -Infinity) raises InvalidOperation
+        if self._sign == 1:
+            return context._raise_error(InvalidOperation,
+                                        'log10 of a negative value')
+
+        # log10(10**n) = n
+        if self._int[0] == 1 and self._int[1:] == (0,)*(len(self._int) - 1):
+            # answer may need rounding
+            ans = Decimal(self._exp + len(self._int) - 1)
+        else:
+            # result is irrational, so necessarily inexact
+            op = _WorkRep(self)
+            c, e = op.int, op.exp
+            p = context.prec
+
+            # correctly rounded result: repeatedly increase precision
+            # until result is unambiguously roundable
+            places = p-self._log10_exp_bound()+2
+            while True:
+                coeff = _dlog10(c, e, places)
+                # assert len(str(abs(coeff)))-p >= 1
+                if coeff % (5*10**(len(str(abs(coeff)))-p-1)):
+                    break
+                places += 3
+            ans = Decimal((int(coeff<0), map(int, str(abs(coeff))), -places))
+
+        context = context._shallow_copy()
+        rounding = context._set_rounding(ROUND_HALF_EVEN)
+        ans = ans._fix(context)
+        context.rounding = rounding
+        return ans
+
     def logb(self, context=None):
         """ Returns the exponent of the magnitude of self's MSD.
 
@@ -3686,20 +3769,23 @@
     def log10(self, a):
         """Returns the base 10 logarithm of the operand.
 
-        >>> ExtendedContext.log10(Decimal('0'))
-        Decimal("-In")
-        >>> ExtendedContext.log10(Decimal('0.001'))
+        >>> c = ExtendedContext.copy()
+        >>> c.Emin = -999
+        >>> c.Emax = 999
+        >>> c.log10(Decimal('0'))
+        Decimal("-Infinity")
+        >>> c.log10(Decimal('0.001'))
         Decimal("-3")
-        >>> ExtendedContext.log10(Decimal('1.000'))
+        >>> c.log10(Decimal('1.000'))
         Decimal("0")
-        >>> ExtendedContext.log10(Decimal('2'))
+        >>> c.log10(Decimal('2'))
         Decimal("0.301029996")
-        >>> ExtendedContext.log10(Decimal('10'))
+        >>> c.log10(Decimal('10'))
         Decimal("1")
-        >>> ExtendedContext.log10(Decimal('70'))
+        >>> c.log10(Decimal('70'))
         Decimal("1.84509804")
-        >>> ExtendedContext.log10(Decimal('+Infinity'))
-        Decimal("Inf")
+        >>> c.log10(Decimal('+Infinity'))
+        Decimal("Infinity")
         """
         return a.log10(context=self)
 


More information about the Python-checkins mailing list