1

hey I am making small C++ program to calculate the value of sin(x) till 7 decimal points but when I calculate sin(PI/2) using this program it gives me 0.9999997 rather than 1.0000000 how can I solve this error? I know of little bit why I'm getting this value as output, question is what should be my approach to solve this logical error?

here is my code for reference

#include <iostream>
#include <iomanip>
#define PI 3.1415926535897932384626433832795
using namespace std;

double sin(double x);
int factorial(int n);
double Pow(double a, int b);

int main()
{
    double x = PI / 2;
    cout << setprecision(7)<< sin(x);
    return 0;
}

double sin(double x)
{
    int n = 1;      //counter for odd powers.
    double Sum = 0; // to store every individual expression.
    double t = 1;   // temp variable to store individual expression
    for ( n = 1; t > 10e-7; Sum += t, n = n + 2)
    {
        // here i have calculated two terms at a time because addition of two consecutive terms is always less than 1.
        t = (Pow(-1.00, n + 1) * Pow(x, (2 * n) - 1) / factorial((2 * n) - 1))
            +
            (Pow(-1.00, n + 2) * Pow(x, (2 * (n+1)) - 1) / factorial((2 * (n+1)) - 1));
    }

    return Sum;
}
int factorial(int n)
{
    if (n < 2)
    {
        return 1;
    }
    else
    {
        return n * factorial(n - 1);
    }
}
double Pow(double a, int b)
{
    if (b == 1)
    {
        return a;
    }
    else
    {
        return a * Pow(a, b - 1);
    }
}
setller
  • 43
  • 7
  • 2
    @user207421: Please do not promiscuously close floating-point questions as duplicates of that question. The problem here is due to errors in integer arithmetic; the `int factorial(int)` function overflows, and changing it to `double factorial(int)` results in the program producing the desired, output, “1”. Jumping to the conclusion that a program that includes floating-point code is wrong because of floating-point behavior is a mistake. – Eric Postpischil Mar 23 '21 at 11:20

2 Answers2

1

I see three bugs:

  • 10e-7 is 10*10^(-7) which seems to be 10 times larger than you want. I think you wanted 1e-7.

  • Your test t > 10e-7 will become false, and exit the loop, if t is still large but negative. You may want abs(t) > 1e-7.

  • To get the desired accuracy, you need to get up to n = 7, which has you computing factorial(13), which overflows a 32-bit int. (If using gcc you can catch this with -fsanitize=undefined or -ftrapv.) You can gain some breathing room by using long long int which is at least 64 bits, or int64_t.

Nate Eldredge
  • 48,811
  • 6
  • 54
  • 82
1

sin(PI/2) ... it gives me 0.9999997 rather than 1.0000000

For values outside [-pi/4...+pi/4] the Taylor's sin/cos series converges slowly and suffers from cancelations of terms and overflow of int factorial(int n)**. Stay in the sweet range.

Consider using trig properties sin(x + pi/2) = cos(x), sin(x + pi) = -sin(x), etc. to bring x in to the [-pi/4...+pi/4] range.

Code uses remquo (ref2) to find the remainder and part of quotient.

// Bring x into the -pi/4 ... pi/4  range (i.e. +/- 45 degrees)
// and then call owns own sin/cos function.
double my_wide_range_sin(double x) {
  if (x < 0.0) {
    return -my_sin(-x);
  }
  int quo;
  double x90 = remquo(fabs(x), pi/2, &quo);
  switch (quo % 4) {
    case 0:
      return sin_sweet_range(x90);
    case 1:
      return cos_sweet_range(x90);
    case 2:
      return sin_sweet_range(-x90);
    case 3:
      return -cos_sweet_range(x90);
  }
  return 0.0;
}

This implies OP needs to code up a cos() function too.


** Could use long long instead of int to marginally extend the useful range of int factorial(int n) but that only adds a few x. Could use double.

A better approach would not use factorial() at all, but scale each successive term by 1.0/(n * (n+1)) or the like.

chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256