# Trouble getting loop to break

Dick Moores rdm at rcblue.com
Tue Nov 20 19:35:45 CET 2007

```At 03:53 AM 11/20/2007, Fredrik Johansson wrote:
>On Nov 20, 2007 8:41 AM, Dick Moores <rdm at rcblue.com> wrote:
> > I'm writing a demo of the infinite series
> >
> > x**0/0! + x**1/1! + x**2/2! + x**3/3! + ...   = e**x   (x is non-negative)
> >
> > It works OK for many x, but for many the loop doesn't break. Is there
> > a way to get it to break where I want it to, i.e., when the sum
> > equals the limit as closely as the precision allows?
> >
> > Here's what I have:
> >
> > ======= series_xToN_OverFactorialN.py ==========================
> > #!/usr/bin/env python
> > #coding=utf-8
> > # series_xToN_OverFactorialN.py  limit is e**x   from p.63 in The
> > Pleasures of Pi,e
> > from mpmath import mpf, e, exp, factorial
> > import math
> > import time
> > precision = 100
> > mpf.dps = precision
> > n = mpf(0)
> > x = mpf(raw_input("Enter a non-negative int or float: "))
> > term = 1
> > sum = 0
> > limit = e**x
> > k = 0
> > while True:
> >      k += 1
> >      term = x**n/factorial(n)
> >      sum += term
> >      print "     sum = %s k = %d" % (sum, k)
> >      print "exp(%s) = %s" % (x, exp(x))
> >      print "  e**%s = %s" % (x, e**x)
> >      print
> >      if sum >= limit:
> >          print "math.e**%s = %f" % (x, math.e**x)
> >          print "last term = %s" % term
> >          break
> >      time.sleep(0.2)
> >      n += 1
> >
> > """
> > Output for x == mpf(123.45):
> > sum =
> >
> 410822093109668964239148908443317876138879647013995774.2951431466270782257597573259486687336246984942
> > k = 427
> > exp(123.45) =
> >
> 410822093109668964239148908443317876138879647013995774.2951431466270782257597573259486687336246984942
> >    e**123.45 =
> >
> 410822093109668964239148908443317876138879647013995774.2951431466270782257597573259486687336246984942
> > """
> > ====================================================
> >
> > This is also on the web at <http://python.pastebin.com/f1a5b9e03>.
> >
> > Examples of problem x's: 10, 20, 30, 40, 100, 101
> > Examples of OK x's: 0.2, 5, 10.1, 11, 33.3, 123.45
> >
> > Thanks,
> >
> > Dick Moores
>
>Hi,
>
>Checking that sum >= e**x will generally not work, because e**x might
>have been rounded up while the sum might repeatedly be rounding down.
>If this happens, no matter how many terms you add, the sum will never
>reach the limit (one of the curses of finite-precision arithmetic).
>
>One solution is to use directed rounding. First compute the limit with
>downward rounding:
>
>mpf.round_down()
>limit = e**x
>mpf.round_default()
>
>Then compute every term in the sum with upward rounding:
>
>mpf.round_down()
>fac = factorial(n)
>mpf.round_up()
>term = x**n / fac
>sum += term
>
>(Note that the factorial should be rounded down to obtain upward
>rounding in the term, since you're taking its reciprocal.)
>
>This should guarantee that the sum eventually exceeds the limit.
>
>As a simpler, less rigorous alternative, instead of checking if sum >=
>limit, check (for example) whether
>
>     abs(sum - limit) / limit <= mpf(10)**(-precision+3)
>
>i.e., if the sum is within 3 digits of the limit. This is the usual
>way to test for numerical equality of floating-point numbers.

I tried out both ways, and found that the second one best suited my
purposes. Please see the 2 highlighted lines in
<http://python.pastebin.com/fcc23b10>
Note that to break the loop I found that this does the job:

if abs(sum - limit) / limit <= mpf(10)**(-precision+1):    #  I