tags:

views:

1235

answers:

4

We are supposed to calculate e^x using this kind of formula:

e^x = 1 + (x ^ 1 / 1!) + (x ^ 2 / 2!) ......

I have this code so far:

while (result >= 1.0E-20 )
{
    power = power * input;
    factorial = factorial * counter;
    result = power / factorial;
    eValue += result;
    counter++;
    iterations++;
}

My problem now is that since factorial is of type long long, I can't really store a number greater than 20! so what happens is that the program outputs funny numbers when it reaches that point ..

The correct solution can have an X value of at most 709 so e^709 should output: 8.21840746155e+307

The program is written in C++.

+4  A: 

What happens if you change the type of factorial from long long to double?

Greg Hewgill
trying to calculate e^709 with factorial of type double produced: -1.#INDAnd the max iteration i guess is 172
Charles Khunt
+27  A: 

Both x^n and n! quickly grow large with n (exponentially and superexponentially respectively) and will soon overflow any data type you use. On the other hand, x^n/n! goes down (eventually) and you can stop when it's small. That is, use the fact that x^(n+1)/(n+1)! = (x^n/n!) * (x/(n+1)). Like this, say:

term = 1.0;
for(n=1; term >= 1.0E-10; n++)
{
    eValue += term;
    term = term * x / n;
}

(Code typed directly into this box, but I expect it should work.)

Edit: Note that the term x^n/n! is, for large x, increasing for a while and then decreasing. For x=709, it goes up to ~1e+306 before decreasing to 0, which is just at the limits of what double can handle (double's range is ~1e308 and term*x pushes it over), but long double works fine. Of course, your final result ex is larger than any of the terms, so assuming you're using a data type big enough to accommodate the result, you'll be fine.

(For x=709, you can get away with using just double if you use term = term / n * x, but it doesn't work for 710.)

ShreevatsaR
+1: Beat me to it!
gnovice
Sorry for being dense but I can't get it .. When I substitute X with 709, the answer is 1.8046..e+016
Charles Khunt
Yeah, I added a note to the answer to address this. x=709 is at the limits of what double can handle.
ShreevatsaR
Oh.. Thanks!! If it's not too much, can you explain the math that you just presented? Not sure how a simpleterm * x / n did it .. my initial solution was obviously a lot longer than what it should be
Charles Khunt
The math is not too much :) The terms are 1, x/1, x*x/(1*2), x*x*x/(1*2*3), x*x*x*x/(1*2*3*4), and so on. That is, the x^2/2! term is (x/2) times the x^1/1! term, the x^3/3! term is (x/3) times the x^2/2! term, etc. In general, x^n = x^(n-1)*x and n!=(n-1)!*n (this is by definition of the factorial), so x^n/n! = x^(n-1)*x/((n-1)!*n) = [x^(n-1)/(n-1)!] * (x/n). That is, the x^n/n! term is x/n times the previous term.
ShreevatsaR
Or, to put it differently -- your code had "power = power * input; factorial = factorial * counter; result = power / factorial;" while mine just has "result = result * input / counter;". Same thing.
ShreevatsaR
Ah.. It's crystal to me now, thanks for the code and the good explanation, i finally understood it!
Charles Khunt
very elegant solution!
Nathan Fellman
+1  A: 

What you presented here is an application of Horner scheme to calculate polynomials.

Tomek
+2  A: 

I can think of another solution. Let pow(e,x) = pow(10, m) * b where b is >=1 and < 10, then

m = trunc( x * log10(e) )

where in log10(e) is a constant factor.

and

b = pow(e,x)/pow(10, m ) = pow(e,x)/pow(e,m/log10(e)) = pow (e,x-m/log10(e))

By this you get:

z = x-m/log10(e)

which will be in between 0 to 3 and then use b = pow(e,z) as given by SreevartsR.

and final answer is

b is base(significant digit) and m is mantissa (order of magnitude).

this will be faster than SreevartsR approach and you might not need to use high precisions.

Best of luck.

This will even work for when x is less than 0 and a bigger negative, in that case z will be in between 0 to -3 and this will be faster than any other approach.

Since z is -3 to 3, and if you require first 20 significant digits, then pow(e,z) expression can be evaluated upto 37 terms only since 3^37/37! = ~ 3.2e-26.

lakshmanaraj
Certainly it will be faster – even faster would be to write `exp(x)` — but it doesn't satisfy the requirement of "calculating e^x without using any functions". :-)
ShreevatsaR
@ShreevatsaR: Actually, I think it does - even though I can't see any advantage from calculating in base-10. Where does this solution use _any functions_?
jpalecek
@jpalecek: pow() is an inbuilt function, just like exp(). I assume the intent of the original poster was precisely *not* to use exp() or other such functions (or even, perhaps, the intent was to use the given series).
ShreevatsaR
@ShreevatsaR: You cannot do `pow(10, integer)` without actually calling `std::pow`? It seems like a kindergarten task for me...
jpalecek
@jpalecek: Sure you can. But then in that case I don't understand what the solution in this answer is. Assuming you don't use any functions for trunc(), and hard-code the constant log10(e), it seems to still require calculating b=pow(e,z). So it has reduced the problem to… another instance of itself?
ShreevatsaR
@ShreevatsaR: Yes - sort of. The point is, Taylor series converges faster in the neighbourhood of zero (and uniformly on any disc around zero) so it's easier to compute (compare your "largest term" which is the 700th with the 37 terms stated here).
jpalecek
@jpalecek: Oh, now I get it; I was misled by the words "I can think of another solution" and "this will be faster than _ approach", and thought it wasn't using the series, and was another solution to the problem of how to calculate e^x. Instead, it is another solution to the problem of the terms getting too large. Thanks, I read this carefully only now. :-) (I wonder if there's a problem of the terms getting too *small* now, but I guess not. Upvoted.)
ShreevatsaR
In this apporoach, I am mimimizing number of terms to expand tailor series and I am using only the constant log10(e) and I am not using any other functions..... and It will be faster than any other approach thats all.
lakshmanaraj