2
$\begingroup$

This is a problem that my friend has. He says he has to create a C++ program to evaluate the series (He says we must use functions):

$$\displaystyle\large\sin x\approx x- \frac{x^3}{3! }+\frac{x^5}{5!}-\frac{x^7}{7!}\cdots\pm\frac{x^n}{n!}$$

The values of $x$ and $n$ have to be taken from the user.

Only I'm not good with C++. Can anyone help?

  • 0
    LaTeX tips: Please don't use display style or large in titles. Also, you can have centered equations by enclosing the LaTeX between `$$ .. $$` instead of `$ .. $`.2012-08-19
  • 0
    I'd suggest changing the series to $x - \frac{x^3}{3!} + \frac{x^5}{5!} - \frac{x^7}{7!}+ \ldots \pm \frac{x^n}{n!}$, to avoid any confusion.2012-08-19
  • 0
    Post updated. :)2012-08-19
  • 0
    And now, just to annoy you, it'd make sense to change $=$ to $\approx$.2012-08-19
  • 0
    Why? (Sorry I'm not good with math)2012-08-19
  • 1
    You'd change it to \approx because the series is *approximating* sin(x), not evaluating it exactly.2012-08-19
  • 1
    @SiliconCelery I think it would be more appropriate to write $$\displaystyle\large\sin x\approx x- \frac{x^3}{3! }+\frac{x^5}{5!}-\frac{x^7}{7!}+\cdots +(-1)^{n}\frac{x^{2n+1}}{(2n+1)!}$$2012-08-19
  • 0
    @Argon, you're right, I only thought of that after an answer had been accepted.2012-08-19
  • 0
    Voted to close. This forum is already full of math homework, we don't need computer homework as well!2012-08-19

4 Answers 4

5

As $T_n=\frac{x^{2n+1}}{(2n+1)!}$ and $T_1=x$

So, $T_{n-1}=\frac{x^{2n-1}}{(2n-1)!}$

So, $T_n=T_{n-1}\frac{x^2}{(2n+1)2n}$

I have utilized this so that we don't need to calculate the powers of x and the factorial every time.

I've assumed that the number of terms is not required as by repeated division, y will eventually be reduced 0 due to the limited precision of double datatype.

Also, x is to be measured in radian(not in degree), as prescribed by the Taylor Series.

      void main(int argc, char **argv)       {           double x , sinx, y;           printf("enter x:\n");           scanf("%lf",&x);            sinx=y=x;           int sign = -1;          for (int idx = 1;; idx++) {              y = y * (x / (2 * idx)) * (x/(2 * idx - 1));              if (y == 0)                 break;              sinx = sinx + sign * y;              sign = -sign;          }          printf("\n\nsin(%lf) = %lf", x, sinx);       } 
  • 0
    For really good performance, one may look into http://www.netlib.org/fdlibm/s_sin.c and http://www.netlib.org/fdlibm/k_sin.c (by Sun Microsystems), http://sourceware.org/git/?p=glibc.git;a=blob_plain;f=sysdeps/ieee754/dbl-64/s_sin.c;hb=HEAD (by IBM --> this's hardly readable)2012-08-19
  • 0
    Also, since OP asked for C++ code it might be more appropriate to use `std::cin >> x` and `std::cout << (...) << std::endl;` for input and output operations2012-08-19
3

If you are looking for speed, don't recompute the factorials or powers.

// Assumes n > 0 double sin(double x, int n) {     const double x_squared = x*x;     int term_num = 1;     double sum = 0.0;     double current_power_of_x = x;     double current_factorial = 1.0;     double sign = 1.0;     while(term_num <= n) {         sum += sign * current_power_of_x / current_factorial;         current_power_of_x *= x_squared;         current_factorial *= (term_num + 2.0) * (term_num + 1.0);         sign *= -1.0;         term_num += 2.0;     }      return sum; } 
  • 0
    Since we're talking C++, some template metaprogramming could speed up the calculation as well.2012-08-19
  • 0
    It should be noted that nullUser here is implicitly using the Horner scheme to evaluate the truncated sine series.2012-08-19
  • 0
    (I have never heard of "Horner scheme" before, though you are correct that the wiki page describes what I am doing). I would just call it "caching partial results" which is commonplace for programmers.2012-08-19
3

Too long for a comment:

The recurrence method in lab bhattacharjee's or nullUser's answers should be preferred over Jack's as it avoids computing expensive factorial terms repeatedly. Some more tweaks would significantly improve this program.

As is, the program will output rubbish for any input of $|x|>10.$ The series will be adding and subtracting very large numbers whose decimal digits are truncated, and you'll most likely not even output a value between -1 and 1. We want smaller values of $x$ so that this doesn't happen and the series converges more quickly. Thus the first tweak would be to use the periodicity/symmetry properties of the sine function to reduce the computation of $\sin x$ to the computation of $\sin x_0$ where $x_0$ is some value between $0$ and $\pi/2.$

Another trick is to exploit the identity $\sin (x_0) = 2 \sin (x_0/2) \sqrt{1- \sin^2 (x_0/2)}$ - applying it twice, we need only compute the series with an input between 0 and $\pi/8<0.4.$ In this range, the series converges very quickly; even with $n=2$ the maximum error in the series computation is $ 0.4^7/7! \approx 3 \cdot 10^{-7}.$ With $n=3$ the maximum error is $0.4^9/9! \approx 7 \cdot 10^{-10}.$

  • 0
    could you please share a few of the tricks for better performance. Also, I observed that the value which is calculated by my code significantly differs from that returned by java.lang.Math.sin(double). How do they calculate?2012-08-19
  • 0
    @labbhattacharjee I've given the descriptions above, the reason I haven't given the code for them is because I have no idea how to program in C or Java. And because of that, I'm not sure how your code or java.lang.Math.sin(double) works either.2012-08-19
2

If your friend's function evaluates it to a certain number of terms, this should work:

double sin(double x, int terms) {     if(terms == 1)         return x;     double result = 0;     for(int j = 1; j <= terms; j++) {         if (j % 2 == 1)             result += pow(x, j * 2 - 1) / factorial(j * 2 - 1);         else             result -= pow(x, j * 2 - 1) / factorial(j * 2 - 1);     }     return result; }  long factorial(int num) {     long result = 1;     for(int j = num; j > 1; j--)         result *= j;     return result; } 

Sorry if this looks funny--posting from my iPhone.

  • 0
    Watch out for numerical problems for large x. The terms will first grow and then get small, and the alternating sum will cause problems. You can ensure x is from [-pi, pi] by something like x -= 2*pi*int(x/(2*pi)). Also, seeing that definition of factorial makes me cringe. Start with fact = 1 and then do fact *= 2*j*(2*j+1).2012-08-19