0

I'm doing a homework assignment where I need to use the McLaurin Series to evaluate for cosh(x) ans sin(x). Each time the program runs, the user inputs a number from 0-10 to specify how many terms to evaluate the series too. The user then inputs the value of x to evaluate to. Once the user hits enter, 10 increments of the series will be calculated and printed out. In addition, I have to find the exact error as well. To do this, I created several different functions for both sinh and cosh that performed the calculations of a specific term. For example: the code below evaluates the term up to 10!.

void cosFunction10(double power, double value)
{
     double cosh_x = 0;
     double math_cosh = 0;
     double exact_error;
     double x;

     if (value < 0)
         x = -0.1*0;

     for (int i = 0; i < 11; ++i)
     {
         cosh_x = (1.0 + ((x*x)/(2.0*1.0)) + ((x*x*x*x)/(4.0*3.0*2.0*1.0)) + ((x*x*x*x*x*x)/(6.0*5.0*4.0*3.0*2.0*1.0)) + ((x*x*x*x*x*x*x*x)/(8.0*7.0*6.0*5.0*4.0*3.0*2.0*1.0)) + ((x*x*x*x*x*x*x*x*x*x)/(10.0*9.0*8.0*7.0*6.0*5.0*4.0*3.0*2.0*1.0)));
         math_cosh = cosh(x);
         exact_error = (math_cosh - cosh_x);

         cout << setiosflags(ios::scientific) << setprecision(3) << x << "\t";
         cout << setiosflags(ios::scientific) << setprecision(5) << cosh_x << "\t";
         cout << math_cosh << "\t";
         cout << exact_error << endl;
         x = x + (0.1*value);
     }
 }

If I run the program and put in these values: 10 (the nth term) and -6 (value for x).

This is what I should get: (2nd increment)

 x                Series            Exact (using the cmath functions)    Exact % Error
 -6.000e-001      1.18547e+000      1.18547e+000                        0.00000e+000

The next 2 out puts would be the same for the exact error, until I get to the 4th increment

 x                Series            Exact (using the cmath functions)    Exact % Error
 -1.800e+100      3.10747e+000      3.10747e+000                        -7.67243e-005

When I run my code, I do not get the above results: (2nd increment)

 x                Series            Exact (using the cmath functions)    Exact % Error
-6.000e-001      1.18547e+000      1.18547e+000                         4.55325e-012

So it seems that something is going wrong because my exact error isn't even close to the same as to what I'm given. I understand that there may be slightly different values, which is expected but not to this extent.

Any suggestions would be much appreciated,

Thank you

user1186173
  • 565
  • 3
  • 7
  • 26
  • 4
    What on earth makes you believe that cosh(x) is the same as 1 + x^2/2? – Kerrek SB Oct 13 '12 at 03:30
  • the actual function runs through a for loop that evaluates up to the nth term. Its for school. – user1186173 Oct 13 '12 at 03:32
  • 1
    You are asking us to find a bug in code you haven't shown us. The bug is in the actual function. – David Schwartz Oct 13 '12 at 03:35
  • 3
    *"the problem is the subtraction isn't evaluating properly"*. I guarantee you the problem is in your logic and not in the subtraction of double precision numbers. If you don't post your actual logic no one can help you. – verdesmarald Oct 13 '12 at 03:35
  • I understand, but this is a case where i'm just not subtracting properly and I'm not sure why. cosh_x and math_cosh produce the correct answers, it's only when I'm subtracting that it doesn't work. And the above code is my function – user1186173 Oct 13 '12 at 03:51
  • 1
    What values do you actually get for `exact_error`? You are printing them in scientific notation, are you sure you are not misreading them perhaps? – jogojapan Oct 13 '12 at 03:57
  • 2
    Your `for` loop does the exact same thing 11 times. Also, why `2.0*1.0` instead of just `2.0`? – David Schwartz Oct 13 '12 at 03:58
  • I thought of that, but when I print them out, and compare them with the actual answers I'm supposed to be getting, they do match. So this is why I'm confused. – user1186173 Oct 13 '12 at 03:58
  • Yes, you said so. That's why I'd like to see what values you get for `exact_error`. I suspect they may actually be very close to 0. – jogojapan Oct 13 '12 at 03:59
  • @DavidSchwartz, it's because it's a factorial of 2. And I have several functions for different terms, but they are all the same just more or less terms And you're right, I forgot to add the last portion, sorry – user1186173 Oct 13 '12 at 03:59
  • They're way off, it should be around 0, I agree, but it prints it out as 4.something – user1186173 Oct 13 '12 at 04:01
  • 1
    What's the _something_, including the _e-..._ part at the very end? – jogojapan Oct 13 '12 at 04:02
  • @jogojapan 4.55325e-012. The result should be 0 – user1186173 Oct 13 '12 at 04:04
  • 2
    Now that is the same as 0.00000000000455325. See http://en.wikipedia.org/wiki/Scientific_notation. – jogojapan Oct 13 '12 at 04:05
  • I mean no disrespect, but given the problem is related to an incorrect interpretation of an otherwise correct output, I'll vote to close this question. – jogojapan Oct 13 '12 at 04:06
  • It's not incorrect, the answer is supposed to be flat 0.0000e+000. It's not doing that, which is what I'm try to figure out. – user1186173 Oct 13 '12 at 04:08
  • 1
    @user1186173: I think you could have made this easier by: 1) showing the actual code (using copy-paste) and 2) showing what you were using for input and what you were getting for output. This would have made it easier for people to discover the actual problem (which was not the subtraction). – Brent Bradburn Oct 13 '12 at 04:41
  • I suggest that you change from scientific to fixed precision -- with a sufficient number of digits. The scientific notation seems to be confusing the issue. For example, where in the world did this come from: "-1.800e+100"? Is it just a typing mistake? – Brent Bradburn Oct 13 '12 at 17:42
  • Also, how do you know what you "should get"? Is it just your understanding of the assignment? – Brent Bradburn Oct 13 '12 at 17:43
  • Yes, I have an example output of the same program my teacher made, so it should be almost exactly the same as his. – user1186173 Oct 13 '12 at 17:54
  • @nobar its an increment. It starts at 0, and then you have to add the value to it after an iteration. So in my example, the first iteration of x would be 0, the second would be -6. And it has to be displayed in that format – user1186173 Oct 13 '12 at 17:55
  • The second iteration is not -6 it is -0.6. This is what I am talking about -- the scientific notation is confusing the issue. – Brent Bradburn Oct 13 '12 at 18:02
  • Hmm.. I used ios::fixed instead of ios::scientific and you were right. It actually shows the right results. But I still need the output to be in scientific notation. – user1186173 Oct 13 '12 at 18:14
  • Use setprecision(20) instead of setprecision(5) and you will see that you are not getting an exact match. My point about the notation was that scientific notation is harder to talk about. – Brent Bradburn Oct 13 '12 at 18:26
  • It's still giving me the same thing though, if I use fix, the subtraction and decimal representation is perfect. Is there way to convert the fixed value to scientific? Or is that the same thing as using ios::scientific? – user1186173 Oct 13 '12 at 18:34
  • You are seeing 20 digits for `exact_error` and they are all zero for the case of x=-0.6? That surprises me -- I think you are not looking at the right thing. Understand, that `setprecision()` **does not affect the subtraction**, it only affects how the result of your subtraction is displayed. If you want to limit the precision of the math, see here: [limit floating point precision](http://stackoverflow.com/questions/3383817/limit-floating-point-precision). – Brent Bradburn Oct 13 '12 at 20:25
  • Well I see 0 for the first couple iterations, after that then it's very small decimal numbers. I'll take a look at that though. Thank you so much for your time and patience. – user1186173 Oct 13 '12 at 20:35

5 Answers5

4

There MUST be such a difference because cosh(x) is defined in a different way which is according to the formula given below:

enter image description here

Please see here for more details.

However, if you are calculating the amount based on computation of MacLaurin series, the problem is that you are not accumulating the values in the right way. The below implementation is perhaps what you actually needed:

void cosFunction(double x)
{
    double cosh_x = 1;
    double math_cosh;
    double exact_error;
    double factorial = 1;
    double x_pow = 1;

    for (int i = 0; i < 11; ++i) {
            cosh_x = x_pow/factorial;
            x_pow = x*x*x_pow; 
            factorial = factorial* (2 * i + 1) * (2 * i + 2);
    }

    math_cosh = cosh(x);
    exact_error = (math_cosh - cosh_x);
    cout << setiosflags(ios::scientific) << setprecision(5) << exact_error << endl;

}
RGO
  • 4,586
  • 3
  • 26
  • 40
  • I know but my assignment is to use the McLaurin series to evaluate for cosh(x). So I've already done the mathematical portion before hand, I'm just coding it now and it is correct because I've confirmed it with my teacher. – user1186173 Oct 13 '12 at 03:54
  • 2
    Ok, so the problem is you are not accumulating the errors. – RGO Oct 13 '12 at 03:56
  • But if I subtract the math_cosh by the cosh_x that should produce the exact value. I just want to get the exact value for each iteration – user1186173 Oct 13 '12 at 04:03
  • My understanding is that the whole calculation was wrong. Please see my update with the modified code. – RGO Oct 13 '12 at 04:23
1

As revealed in the comments, the error you get is actually extremely small, less than 10-11. In addition, you must take into account that the way you compute cosh and the way it's done by the built-in function is not the exact same. Both methods are ultimately approximations, but they work in different ways, hence the rounding errors and approximation errors they produce will be slightly different.

You cannot expect the error to be "flat 0".

jogojapan
  • 68,383
  • 11
  • 101
  • 131
  • I understand what you're saying, and I'm not trying to be difficult. I just have a specifc assignment, with output that should be exactly the same as whats given, and whats given is that its flat 0. But I'll just figure something else out. Thank you for all your help and patience – user1186173 Oct 13 '12 at 04:15
  • 1
    In that case the obvious question to ask is: _Does the built-in function use a McLaurin series?_, and then: _How many iterations are needed_? But even if you figure that out, your rounding errors may still be different from the built-in ones. Comparing floating point numbers is difficult, and you can never expect the difference between two of them to be 0: http://stackoverflow.com/questions/10334688/how-dangerous-is-it-to-compare-floating-point-values (Frankly, if the assignment is as you say, there is something wrong with that assignment.) – jogojapan Oct 13 '12 at 04:21
  • Well, to explain it more clearly, the purpose of the assignment is to have a user input a term to evaluate the series to and then input a value for x. Then it does it through 10 increments starting at 0. And I have multiple functions for different increments. So the first couple increments should equal 0. I'm going to ask my teacher about this, in light of your comments. So thank you very much for explaining things more clearly. – user1186173 Oct 13 '12 at 04:32
1

The correct answer may be, in a mathematical sense, zero. But in a practical computing sense, it won't be zero:

Imagine if you're doing arithmetic with fixed decimal precision, say 8 digits. You would represent 1.0/3.0 as .33333333. But that would make 3.0 * (1.0/3.0) turn into 3.0 * .33333333 which would be .999999999. So while in a mathematical sense, 1.0 - (3.0 * (1.0 / 3.0)) should be zero, with fixed decimal precision, you may well get something like .00000001.

The same rules apply to fixed binary precision, which is what you get when you use a double.

David Schwartz
  • 179,497
  • 17
  • 214
  • 278
1
//Part of the problem is that in the line after the 'for' nothing changes (typo?.) The
//following works for x = 0.5`
//Note that the loop works properly for all terms including the first which is 1.0

#include <iostream>
#include <math.h>

using namespace std;

int factorial(int n)
{
    return (n == 1 || n == 0) ? 1 : factorial(n - 1) * n;
}

int main()
{
    // use taylor series to approximate cosh(x) - use 7 terms
    double x = 0.5;
    double top, bot;
    double s = 0.0;

    for (int i = 0; i < 7; i++)
    {
        top = pow(x,double(i)*2.0);
        bot = double(factorial(i*2));
        s  += (top / bot);
    }
    cout << "myCosh     " << s << endl;
    cout << "cosh       " << cosh(x)<< endl;
    cout << "difference " << fabs(s - cosh(x)) << endl;
    return 0;
}
Marichyasana
  • 2,966
  • 1
  • 19
  • 20
0

I ran your function with the cout operation modified as follows:

cout << setiosflags(ios::fixed) << setprecision(12);
cout << "for x="<<x<<": "<<math_cosh<<" - "<<cosh_x<<" = "<<exact_error<<"\n";

I chose my own value for value, since you didn't indicate what you were using.

I get the following result, which demonstrates that math_cosh and cosh_x are not the same. Your problem seems to be that you didn't see enough digits to notice that they were different.

for x=0.003233200000: 1.000005226796 - 1.000005226791 = 0.000000000005
for x=0.006466400000: 1.000020907237 - 1.000020907164 = 0.000000000073
for x=0.009699600000: 1.000047041489 - 1.000047041120 = 0.000000000369
for x=0.012932800000: 1.000083629824 - 1.000083628658 = 0.000000001166
for x=0.016166000000: 1.000130672624 - 1.000130669778 = 0.000000002846
for x=0.019399200000: 1.000188170381 - 1.000188164480 = 0.000000005901
for x=0.022632400000: 1.000256123697 - 1.000256112765 = 0.000000010932
for x=0.025865600000: 1.000334533282 - 1.000334514632 = 0.000000018650
for x=0.029098800000: 1.000423399955 - 1.000423370081 = 0.000000029875
for x=0.032332000000: 1.000522724646 - 1.000522679112 = 0.000000045534
for x=0.035565200000: 1.000632508392 - 1.000632441726 = 0.000000066667

Regarding the homework assignment, to calculate cosh(x) using the Maclaurin series: Your function, as written, is not very precise since it only computes the first three terms of the series 1 + 0 + x*x/2. You are not computing x^n or n! beyond n=2. To generate a more precise result, you need to be accumulating more terms of the series -- as in the following...

int NUMBER_OF_TERMS=10; // the number of terms to compute (including zero terms)
for ( int i=0; i<NUMBER_OF_TERMS; i++ )
   {
   cosh_x += (i%2==0?1:0)*term; // even derivatives are 1, odd derivatives are 0
   term *= x/(i+1); // apply incremental power and factorial
   }

This still doesn't guarantee exactness. Due to rounding errors, you can't be sure to perfectly match the built-in function cosh(x) unless you duplicate its algorithm exactly. Both functions will only produce an approximation, since for most input values, the pure mathematical output would be a transcendental number -- and such a number cannot be exactly represented in a double.

You should also keep in mind that the Maclaurin series is a polynomial-based approximation which is most accurate near x=0. As is the nature of polynomials, an incomplete summation may eventually (as x moves away from 0) diverge very rapidly from the correct answer. This is why, in the example output above, the error values are smaller for smaller values of x.


Regarding scientific notation vs. fixed-point precision:

Scientific notation shows you the most significant non-zero digits of a number -- even if those digits start 12 places to the right of the decimal point. When reading scientific notation, it is important to be aware of the information after the e since this describes the true magnitude of the number. double-precision values can store approximately 20 decimal digits of real information. This is nice for doing many repeated calculations, but it usually isn't very meaningful when comparing two numbers for equality.

What you can do, although this may not be in the scope of your assignment, is limit the fixed-point precision of the difference. Here is an example of how you might do that:

const double PRECISION=1e-4; // indicates the number of fractional digits to keep
exact_error = round(exact_error/PRECISION)*PRECISION; // limit precision

...or it might be simpler to just check for the close-to-zero case ...

if ( fabs(exact_error)<PRECISION/2 ) exact_error = 0; // close enough to zero
Brent Bradburn
  • 51,587
  • 17
  • 154
  • 173
  • [cplusplus.com - std::fixed](http://www.cplusplus.com/reference/iostream/manipulators/fixed) shows that there are three different options for the `floatfield` formatting flag to iostreams. These are `fixed`, `scientific`, and `(none)`. Each of these interacts in their own way with the `precision` value. – Brent Bradburn Oct 18 '12 at 15:09