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

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


Author: facundo.batista
Date: Tue Aug 14 22:32:37 2007
New Revision: 57038

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

Coded the ln() 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:32:37 2007
@@ -2625,9 +2625,86 @@
         else:
             return Decimal(1)
 
+    def _ln_exp_bound(self):
+        """Compute a lower bound for the adjusted exponent of self.ln().
+        In other words, compute r such that self.ln() >= 10**r.  Assumes
+        that self is finite and positive and that self != 1.
+        """
+
+        # for 0.1 <= x <= 10 we use the inequalities 1-1/x <= ln(x) <= x-1
+        adj = self._exp + len(self._int) - 1
+        if adj >= 1:
+            # argument >= 10; we use 23/10 = 2.3 as a lower bound for ln(10)
+            return len(str(adj*23//10)) - 1
+        if adj <= -2:
+            # argument <= 0.1
+            return len(str((-1-adj)*23//10)) - 1
+        op = _WorkRep(self)
+        c, e = op.int, op.exp
+        if adj == 0:
+            # 1 < self < 10
+            num = str(c-10**-e)
+            den = str(c)
+            return len(num) - len(den) - (num < den)
+        # adj == -1, 0.1 <= self < 1
+        return e + len(str(10**-e - c)) - 1
+
+
     def ln(self, context=None):
         """Returns the natural (base e) logarithm of self."""
 
+        if context is None:
+            context = getcontext()
+
+        # check context and operand
+        ans = self._checkMath(context)
+        if ans:
+            return ans
+
+        # ln(NaN) = NaN
+        ans = self._check_nans(context=context)
+        if ans:
+            return ans
+
+        # ln(0.0) == -Infinity
+        if not self:
+            return negInf
+
+        # ln(Infinity) = Infinity
+        if self._isinfinity() == 1:
+            return Inf
+
+        # ln(1.0) == 0.0
+        if self == Decimal(1):
+            return Decimal((0, (0,), 0))
+
+        # ln(negative) raises InvalidOperation
+        if self._sign == 1:
+            return context._raise_error(InvalidOperation,
+                                        'ln of a negative value')
+
+        # result is irrational, so necessarily inexact
+        op = _WorkRep(self)
+        c, e = op.int, op.exp
+        p = context.prec
+
+        # correctly rounded result: repeatedly increase precision by 3
+        # until we get an unambiguously roundable result
+        places = p - self._ln_exp_bound() + 2 # at least p+3 places
+        while True:
+            coeff = _dlog(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 log10(self, context=None):
         """Returns the base 10 logarithm of self."""
 
@@ -3590,16 +3667,19 @@
     def ln(self, a):
         """Returns the natural (base e) logarithm of the operand.
 
-        >>> ExtendedContext.ln(Decimal('0'))
-        Decimal("-Inf")
-        >>> ExtendedContext.ln(Decimal('1.000'))
+        >>> c = ExtendedContext.copy()
+        >>> c.Emin = -999
+        >>> c.Emax = 999
+        >>> c.ln(Decimal('0'))
+        Decimal("-Infinity")
+        >>> c.ln(Decimal('1.000'))
         Decimal("0")
-        >>> ExtendedContext.ln(Decimal('2.71828183'))
+        >>> c.ln(Decimal('2.71828183'))
         Decimal("1.00000000")
-        >>> ExtendedContext.ln(Decimal('10'))
+        >>> c.ln(Decimal('10'))
         Decimal("2.30258509")
-        >>> ExtendedContext.ln(Decimal('+Infinity'))
-        Decimal("Inf")
+        >>> c.ln(Decimal('+Infinity'))
+        Decimal("Infinity")
         """
         return a.ln(context=self)
 


More information about the Python-checkins mailing list