0

I made a c++ program that calculates sin without math.h. Im using this algorithm for my program https://ibb.co/bTnQnS. I enter 45 degrees, the program converts degrees to radians, the program uses the algorithm, and the program outputs -0.868597. The program should output 0.70710678 or √2/2. What am I doing wrong with the algorithm?

Code:

#include "stdafx.h"
#include <iostream>

using namespace std;

double sin(int input, int decimal_count);
int factorial(int n);
double deg_to_rad(int deg);
double power(double base, int power);


int main(){
    double angle;
    int decimal;

    cout << sin(45,8) << endl;

    //end
    system("pause");
    return 0;
}

double sin(int input, int accuracy) {
    int odds = 3;
    double sin;
    double rads = deg_to_rad(input);

    for (int i = 1; i <= accuracy; i += 1) {


        if (i==1) {
            sin = power(rads, odds) / factorial(odds);
        }
        else if (i%2==0) { 
            sin = (power(rads, odds) / factorial(odds)) + sin; 

        }
        else {
            sin = (power(rads, odds) / factorial(odds)) - sin;

        }
        odds = odds + 2;

    }
    sin = sin - rads;
    return sin;
}



int factorial(int n) {
    int fact = 1;

    for (int j = 1; j <= n; j+=1) {
        fact = fact * j;
    }
    return fact;
}



double deg_to_rad(int deg) {
    return deg*(3.14159265/180);
}


double power(double base, int power) {
    double ans = 1;

    for (int k = 1; k <= power; k+=1) {
        ans = ans * base;
    }

    return ans;
}

1 Answers1

3

your taylor series expansion function is incorrect. :)

you have to disregard all even terms.

I have fixed it for you (i removed some windows specific stuff as I don;t have a windows machine: the stdfax.h header and the calls to pause were removed)

# include <cstdlib>
# include <iostream>

using namespace std;

double sin(int input, int decimal_count);
int factorial(int n);
double deg_to_rad(int deg);
double power(double base, int power);


int main(){
    double angle;
    int decimal;

    cout << "The sine value is: " << sin(45,8) << endl;

    //end
    system("sleep 2");
    return 0;
}

double sin(int input, int accuracy) {
    int odds = 3;
    double sin;
    double rads = deg_to_rad(input);
    bool negative_flag = true;
    cout << "You entered " << input << " degrees" << endl;
    cout << "This is " << rads << " radians" << endl;
    sin = rads;

    for (int taylor_term = 3; taylor_term <= 7; taylor_term += 2) {
        double term = (double)(power(rads, taylor_term) / factorial(taylor_term));
        if (negative_flag) {
            term = -1 * term;
        }
        negative_flag = !(negative_flag);
        sin += term;
    }
    return sin;
}



int factorial(int n) {
    int fact = 1;

    for (int j = 1; j <= n; j+=1) {
        fact = fact * j;
    }
    return fact;
}

Running this output

You entered 45 degrees
This is 0.785398 radians
The sine value is: 0.707106

Explanation

The taylor series expansion for sine is a series of terms with odd taylor's coefficients that alternate in sign. In my code the alternating signs is effected by the flag. I've also taken into account only the first 3 terms of the taylor series expansion.

Other than that, the line double term = (double)(power(rads, taylor_term) / factorial(taylor_term)); calculates every term in the taylor series expansion.

negative_flag = !(negative_flag); resets the flag sign for the next term.

Addressing your comment and where your code was a bit wrong

Below is your sin func with minimal changes to make it work. What you were doing wrong

These are just minimal edits, performing these edits would naturally be followed up with some code style cleanup. eg: the if and else block(not else if) have almost the exact same code

  1. sin was not being initialized before being modified
  2. the attribution to correct signs the taylor terms in the if blocks was not correct.
  3. the extra subtraction of rads at the end from sin was not required. Once these things were fixed, your code works :)

    int odds = 3;
    double sin ;
    double rads = deg_to_rad(input);
    sin = rads;
    
    for (int i = 1; i <= accuracy; i += 1) {
    
    
        if (i==1) {
            sin = sin - power(rads, odds) / factorial(odds);
        }
        else if (i%2==0) {
            sin = (power(rads, odds) / factorial(odds)) + sin;
    
        }
        else {
            sin = -(power(rads, odds) / factorial(odds)) + sin;
    
        }
        odds = odds + 2;
    
    }
    return sin;
    
Srini
  • 1,619
  • 1
  • 19
  • 34
  • 2
    You don't need the `negative_flag` at all. All you need is to flip between `1` and -`1` and multiply by that value. `double multiplier = 1.0; ...for(...) {... sin += term * multiplier; multiplier *= -1; }` – PaulMcKenzie Apr 18 '18 at 21:46
  • @Srini Doesnt my "odds" variable skip all even numbers? Why didn't my function work? – Johanson151 Apr 18 '18 at 21:46
  • @PaulMcKenzie yes that seems a bit cleaner :) – Srini Apr 18 '18 at 21:52
  • @Johanson151 I've edited my answer to provide some more explanation about what you were doing wrong :) – Srini Apr 18 '18 at 21:57
  • @Srini Thank you very much! You were very helpful! – Johanson151 Apr 18 '18 at 22:23
  • You don't need factorial and power at all. Since you're iterating over the term ordinal, it is possible to incrementally calculate next term in the series by simple multiplication/division. – Red.Wave Apr 19 '18 at 05:42
  • That's a great observation and a good suggestion for optimization. – Srini Apr 19 '18 at 05:54