nan in sin(x) code

Oct 8, 2017 at 1:40pm
Question
Q16. Write a program to evaluate sine value of pi/2 in degrees entered by the user using the following series. Prompt the user to enter the number of terms to evaluate.sin(x) = x-x3/3! + x5/5! -….

Problem in Code given below
When number of truncation terms are taken 10, it gives correct output. But when n is declared as 100, it gives nan. Why?

Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include<iostream>
#include<cmath>
using namespace std;
int fact(int n)
{
int fact =1;
int i;
for (i=1;i<=n;i++)
{
fact = fact*i;
}
return fact;	
 } 
int main()
{
int i,n;
float angle, sum = 0; 
cout<<"Enter angle in radians";
cin>>angle;	
cout<<"Enter number of terms for truncation";
cin>>n;
for(i=0;i<n;i++)
{
sum = sum+ (pow(-1,i)*pow(angle, 2*i+1))/fact(2*i+1);
}
cout<<"sum="<<sum;

return 0;
}
Last edited on Oct 8, 2017 at 1:41pm
Oct 8, 2017 at 2:02pm
Ok. I got it. long double should be used instead of int to handle numbers of the order 100.
Oct 8, 2017 at 2:24pm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include<iostream>
#include<cmath>

double fact( unsigned int n )
{
    // https://en.wikipedia.org/wiki/Stirling%27s_approximation#Stirling.27s_formula_for_the_gamma_function
    // http://en.cppreference.com/w/cpp/numeric/math/tgamma
    return std::tgamma(n+1) ;
}

double to_degrees( double radians )
{
    static const double pi = std::atan2( 0, -1 ) ;
    static const double multiplier = 180.0 / pi ;

    return radians * multiplier ;
}

int main()
{
    double angle ;
    std::cout << "Enter angle in radians: " ;
    std::cin >> angle ;

    unsigned int n ;
    std::cout << "Enter number of terms: ";
    std::cin >> n;

    double sum = 0 ;

    for( unsigned int i = 0 ; i < n; ++i )
    {
        const unsigned int m = 2*i + 1 ;
        sum += ( ( i%2 ? -1 : +1 ) * pow( angle, m ) ) / fact(m) ;
    }

    std::cout << angle << " radians (" << to_degrees(angle) << "degrees)\nsum == "
              << sum << "\nstd::sin(" << angle << ") == " << std::sin(angle) << '\n' ;
}
Oct 8, 2017 at 3:20pm
@abcdef123,
It is a common illusion that the factorial being a convenient way of writing the formula means that it makes any sense to calculate it that way. The factorial function grows too fast to make this useful and it's going in the denominator anyway.

It is also highly inefficient to calculate each new term from scratch: each one is a multiple of the previous one with four new factors and a reversal of sign.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include<iostream>
using namespace std;

constexpr double DEGTORAD = 3.14159265358979 / 180.0;

int main()
{
   int i, n;
   double angle, degrees;

   cout << "Enter angle in degrees: ";
   cin >> degrees;
   angle = degrees * DEGTORAD;

   cout <<"Enter number of terms for truncation: ";
   cin >> n;

   double term = angle, sum = term;
   for ( i = 1 ; i < n; i++ )         // you have the 0th term already
   {
      term = -term * angle * angle / ( ( 2 * i ) * ( 2 * i + 1 ) );
      sum += term;
   }

   cout << "sum=" << sum;
}



Enter angle in degrees: 30
Enter number of terms for truncation: 20
sum=0.5


Enter angle in degrees: 90
Enter number of terms for truncation: 100
sum=1

Oct 8, 2017 at 3:34pm
you can just make a lookup vector of factorials and grab the values off a website if you need a fact function. Calculation of them up to 64 bit integers is just spinning the CPU for nothing.
past 64 bits, you need a big-int class or assembly that can use the 128 bit register and it gets a little more 'exiting'.

but here, as noted, you don't even need that; its the previous value * something, and you can sign flip via pow(-1, iteration) or better yet int sign[2] = {-1,1} and a modulo on iteration (watch zero and % in loops, or you'll get a double zero bug, be mindful of it).

computers have limits. Factorials and large powers run it dry fast, n=100 may not be possible to do using conventional data types, and even if it DID give the right answer, you have found more significant digits than you can print in a double. I am guessing (you can determine it yourself) that 10-15 is about as much as you need to get all the digits you can use.

not sure what a long double is (32, 64 bit floating point types are the IEEE types you can use on most hardware and that is float, double) but some compilers support the tenbyte double which is what the FPU uses internally. The *intent* of the 10b is to give extra digits to minimize roundoff errors internally in the FPU. But for some simple algorithms with minimal roundoff you can use the 10b double from the FPU safely. There is a 128 bit format but I don't know what machines and compilers support it. You machine may or may not think a long double is 128. I think some OS / setups do something internally (not on the FPU) that can give oddball precision but more than 80 and less than 128. I never used anything like that, so this is a dim memory.






Last edited on Oct 8, 2017 at 3:50pm
Oct 8, 2017 at 3:54pm
> It is also highly inefficient to calculate each new term from scratch:

>> you can just make a lookup vector of factorials and grab the values off

If the overriding goal of this program is performance, use std::sin()


> give extra digits to minimize roundoff errors internally in the FPU....

If the overriding goal of this program is accuracy to the extent possible, use std::sin()


Finally: If the overriding goal of this program is performance, combined with accuracy to the extent possible:
there are no surprises; use std::sin()
Last edited on Oct 8, 2017 at 3:55pm
Oct 8, 2017 at 8:41pm
Thanks all. I also got to know new efficient codes from this thread. The aim was to compute sin from series expansion without using sin
Topic archived. No new replies allowed.