This is my code for attempting a while Loop to model a pendulum swing, however the values produced don't seem to help me:
#include <stdio.h>
#include <math.h>
#define PI 3.14159265
main()
{
float theta = 0; // initial value for angle
float omega = 0.2; // initial value for angular speed
float time = 0; // initial time
float dt = 0.01; // time step
while (theta < 2 * PI && -2*PI) {
time = time + dt;
theta = theta + omega*dt;
printf("theta=%i, omega=%i, time=%i, and dt=%i\n", theta, omega, time, dt);
}
system("PAUSE");
}
How can I modify this to be more helpful? The while condition I have to use is that it should stop when the pendulum has made one revelation either forwards or backwards (-2PI or 2PI). I need to use the formula 'omega=omega-(g/l)dtsin(theta)' and NOT make the assumption that theta approximately equals sin(theta).
while(theta <2*PI&&-2*PI)
In c if we want to combine multiple expressions we can combine them using logical operators as
while((theta < 2 * PI) && (theta > -2 * PI))
PS- I am no physics expert. Change conditions as per your requirement
There are some problem in this code :
According to the standard you shouldn't use
main()
But either
int main()
int main(void)
int main(int argc, char *argv[])
Also don't forget
return 0;
Sample program :
#include <stdio.h>
int main(void) {
/* Do stuff */
return 0;
}
If you wish to read more information about that, click here.
As LutzL pointed out you should use M_PI instead of your definition of PI. Just #include <math.h>
That's how you would print the value for example :
printf("\nValue of M_PI is %f\n\n", M_PI);
Read more here.
As pointed out by rootkea :
In c if we want to combine multiple expressions we can combine them
using logical operators as
while((theta < 2 * PI) && (theta > -2 * PI))
If you want greater precision (I think that this is the case) you should use double instead of float.
You are trying to print float variables with the "%i" format specifier. That's wrong, "%i" is used to print int signed integer and using the wrong specifier invokes undefined behavior. You should use "%f" to print float or double signed decimal.
Have a read about printf format specifiers here.
The angle equation that you use is not right. According to your expression it changes linearly with time, although the equation is harmonic:
θ = θ0cos(ωt)
where ω = g/L, L is the length of the pendulum and g the acceleration due to gravity.
Where thetaMax is the max deflection angle, g is gravity const, L is length of pendulum and t is time. So, you want to know thetaMaxfrom energy conservation law, for example.
Alrught, let's revise energy conservation law here:
As you know Potential energy at max deflection equals Kinetic energy at the lowest point of the pendulum path:
KE = ПE
m*g*L*sin(thetaMax) = m*sqr(omega)*sqr(L)/2
Approximately, for small angles:
m*g*L*thetaMax = m*sqr(omega)*sqr(L)/2
thetaMax = sqr(omega)*L/(2*g)
that is it. Here is code that calculates this type of oscillatory motion. All absolute values:
#include <stdio.h>
#include <math.h>
#define PI 3.14159265
#define L 1 //length of the pendulum
#define g 9.8 //gravity const
int main()
{
double theta = 0; // initial value for angle
double omega = 0.2; // initial value for angular speed
double time = 0; // initial time
double dt = 0.01; // time step
double thetaMax = omega*omega*L/(2*g);
while (theta < thetaMax) {
time = time + dt;
theta = thetaMax * sin(omega*time);
printf("theta=%f, omega=%f, time=%f, and thetaMax=%f\n", theta, omega, time, thetaMax);
}
return 0;
}
It is going to calculate these parameters infinitely, so just limit theta in the condition of the while-loop to a desired angle of deflection.
The idea of modelling, in simple terms, involves finding a mathematical representation of a physical phenomenon. In other words you need to "find" an equation that represents all the observable properties of the object in context, in your case a pendulum1.
All you need to describe a pendulum is its equation of motion. To find it you firstly try to qualify the motion, by observing it:
If you try to displace it (many times with a very small angle) from its equilibrium point, you will mostly observe few swings, along the same path, where its amplitude decreases and it is brought back to its stable equilibrium, as if by some kind of restoring force, in other words the pendulum performs regular and repeating motion, called periodic motion
Thus, you are looking for a mathematical function that can represent a periodic motion, as it turns out a great candidate for this role are the trigonometric functions: sin and cos, that have the needed property i.e. repeat themselves with a period T.
To find and quantify this restoring force you use Newton's Second Law, where after you express everything in terms of the very small angle theta << 1 (that allows using of the small-angle approximation: sin(theta) = theta) of displacement theta, you get the wanted equation of motion, which is represented by the following differential equation:
with a great surprise (and to the first order of approximation), when you solve the above equation you find:
which approximately2 matches the predictions from your observations (with some random error (standard deviation), preferably not systematic), i.e. the motion of the pendulum is described by the cosine function, it has periodic motion with an amplitude equal to the initial displacement and a period equal to:
and here is the catch,
the above equation for theta describes a pendulum that does not lose energy and its values repeat infinitely and that is why the loop in your program is an infinite loop harmonically oscillating pun intended with the same values!
If you want to observe a motion that slowly stops you need to add an additional damping force that will slowly subtract energy from the system. This damping force is represented by the following additional term in the equation of motion:
that again, can be expressed in terms of the angle theta. Now, when you solve the equation of motion, the amplitude gets multiplied by:
where b is the damping constant from which it depends how fast the motion will stop.
As a consequence of the above,
if you want to see something similar to the real pendulum you need to include this last exponential to your equation.
To do this just follow the great explanation offered by #Petr Stepanov, modifying thetaMax to include the above exponent.
#include <stdio.h>
#include <math.h>
#define PI 3.14159
#define E 2.71828 // Euler's number
#define L 1 // length of the pendulum
#define g 9.80665 // gravity const
int main() {
double theta = 0.0; // initial value for angle
double omega = 0.2; // initial value for angular speed
double time = 0.0; // initial time
double dt = 0.01; // time step
double b = 0.5; // modified damping constant
double thetaMax = omega * omega * L / (2 * g);
while (theta < thetaMax) {
time = time + dt;
theta = thetaMax * ldexp(E, (-b) * time) * sin(omega * time);
printf("Deflection angle=%f, Angular frequency=%f, Time=%f\n", theta, omega, time);
}
return 0;
}
1. An object with mass m, hung on a fixed point with a non-elastic, "massless" thread with length l , that allows it to swing freely in a gravitational field, quantified by the gravitational acceleration constant g.
2. It should be noted that: "all models are wrong; the practical question is how wrong do they have to be to not be useful"
The equation for a (simple, point mass on string) physical pendulum is
θ'' + k·sin(θ) = 0
where the constant:
k = g / L
contains gravitational g and length L.
θ is the angle measured from the position of rest, which of course is pointing down, that is, in a Cartesian coordinate system with the usual angle convention, at -90°. Thus the pendulum oscillations translate to theta oscillations around zero.
You could solve that (conservative) differential equation using symplectic Euler or Verlet/Leapfrog. Symplectic Euler in one variant has the loop (using ω=θ', that is, (θ',ω')=(ω, -k·sin(θ))
for(...) {
omega -= k*sin(theta) * dt;
theta += omega * dt;
t += dt;
printf(...);
}
Related
I am trying to calculate the absolute error in the calculation of f'(1) (where f(x)=sin(x)) caused, due to direct calculation i.e.(cos(1)), and that done with the formula
f'(x) = (f(x+h)-f(x))/h
i.e. Error function = cos(1) - ((sin(1+h)-sin(1))/h), for a small value of h.
But for a certain value of 'h' (say 10^{-8}), when I calculated through a very precise calculator(Kelsan Calculator), the absolute error came to be 4.207355e-9 but computing the same thing using following program:
#include <stdio.h>
#include <math.h>
int main() {
double h = 1e-8;
double a = (sin(1 + h) - sin(1)) / h;
printf("%10e", cos(1) - a);
}
I got 2.969885e-09. Is it possible to calculate this correctly on C?
Once you round two values which are extremely close to begin with and subtract the rounded values, the results are going to be all over the place.
Using a little trigonometry, you can represent the difference of two sines much more accurately.
double a = 2 * cos(1+h/2) * sin(h/2) / h;
This change results in printing exactly 4.207355e-09.
I am a real beginner here and I'm really not sure about this. It is a homework assignment.
We have to find 10 (x,y) coordinates of an arc using the radius, a starting angle and an end angle. The program works, but the results it gives differ very slightly from the 'correct' results as required by the automatic checking system. Here's the code and both mine and the systems results based on r=100, angle1=1, anglef=30!
Thanks in advance!
FILE *pf1;
int n=0;
double angulo1, angulof, angulo, radio, x, y;
printf ("\nIntroduce radio : ");
scanf ("%lf", &radio);
printf ("\nIntroduce angulo inicial : ");
scanf ("%lf", &angulo1);
printf ("\nIntroduce angulo final : ");
scanf ("%lf", &angulof);
angulo = ((angulof-angulo1)/9);
pf1 = fopen("salida.txt", "w");
for (n=0; n<=9; n++)
{
x=radio*cos(angulo1+(angulo*n));
y=radio*sin(angulo1+(angulo*n));
fprintf (pf1, "%lf,%lf\n", x,y);
}
return 0;
.
ERROR
■■■ MY FILE:
54.030231,84.147098
-41.614684,90.929743
-98.999250,14.112001
-65.364362,-75.680250
28.366219,-95.892427
96.017029,-27.941550
75.390225,65.698660
-14.550003,98.935825
-91.113026,41.211849
-83.907153,-54.402111
■■■ CORRECT FILE:
54.030231,84.147095
-41.614685,90.929741
-98.999252,14.112000
-65.364365,-75.680252
28.366219,-95.892426
96.017029,-27.941549
75.390228,65.698662
-14.550003,98.935822
-91.113029,41.211849
-83.907150,-54.402111
The post is very confusing:
Calculating angles from 1 to 30 radians in 10 steps doesn't make much sense. Sensible values for radians are from -2π to +2π, outside that range trigonomic functions quickly lose precision. Also, making steps that are much larger than a few degrees is very unusual. To get from 1 to 30 radians in 10 steps, steps of almost 180° are taken.
Some testing of the output reveals that the steps are smaller: from 1 to 10 radians in 10 steps. This is still 57° per step, and goes almost 2 times around the circle.
Reverse engineering the output with atan2(y,x) reveals that the desired output is less precise than the calculation with doubles. So, probably the calculations used 32 bit floats. To test this, one has to be very careful. Internally, floats can get passed as doubles, and the processor works with 80 bits of precision for arithmetic calculations. (Note that on most machines long double has the same precision as double.)
Now, if you call sin on a float, the compiler often calls the double version of sin. To force the float version, one can try to explicitly call them, they have an f appended to the function name: sinf and cosf.
Testing the following with MicroSoft Visual C, 2017 community edition:
#include <math.h>
void test_sinf()
{
float radio = 100;
float angulo1 = 1;
float angulof = 10;
float angulo = (angulof - angulo1) / 9;
float x, y;
int n;
for (n = 0; n < 10; ++n) {
x = radio * cosf(angulo1 + (angulo*n));
y = radio * sinf(angulo1 + (angulo*n));
printf("%.6lf,%.6lf\n", x, y);
}
}
outputs:
54.030228, 84.147095
-41.614685, 90.929741
-98.999252, 14.112000
-65.364365, -75.680252
28.366220, -95.892426
96.017029, -27.941549
75.390228, 65.698662
-14.550003, 98.935822
-91.113022, 41.211849
-83.907150, -54.402115
This leaves the last digit of only 4 of the numbers with a difference. Which suggests a slightly different library/compiler has been used. As the angles and the radius are all integer numbers which can be represented exact with floats, they are an unprobable cause of the differences.
edit: Testing out the suggestion of #gnasher729 is seems he's right. Running the code with the double precision sin and cos, and convering the result to float before printing, gives exactly the "desired" numbers. This probably gives the same results on most compilers for this test case. (32 bit floats are an IEEE standard, and 64 bit trigonomic functions have enough precision to make implementation details disappear after rounding.)
#include <math.h>
void test_sin_converted_to_float()
{
float radio = 100;
float angulo1 = 1;
float angulof = 10;
float angulo = (angulof - angulo1) / 9;
float x, y;
for (int n = 0; n <= 9; ++n) {
x = radio * cos(angulo1 + (angulo*n));
y = radio * sin(angulo1 + (angulo*n));
printf("%.6lf, %.6lf\n", x, y);
}
}
Checking your data with a spreadsheet, it contains exactly what it ought to contain. The data in the second file however look like sine and cosine where calculated with single precision (float).
Which would mean that whoever created this "automatic checking" should be very, very, ashamed of themselves.
I'm working on a project for our school and we are required to create a program that computes the approximation of the Taylor Expansion Series of sin x and cos x, only using <stdio.h> and without user-defined functions other than int main(), of all angles from -180 to 180 in increments of +5. the following is my code:
#include <stdio.h>
#define PI 3.141592653589
#define NUMBER_OF_TERMS 10
int
main()
{
int cosctr, sinctr;
double ctr, radi;
double cosaccu, costerm, sinaccu, sinterm;
for (ctr = -180; ctr < 185; ctr = ctr + 5) {
radi = ctr * PI/180.0;
cosctr = 1;
cosaccu = 1;
costerm = 1;
sinctr = 2;
sinaccu = radi;
sinterm = radi;
while (cosctr <= 2*NUMBER_OF_TERMS) {
costerm = costerm*(-1)*(radi*radi)/(cosctr*(cosctr + 1));
cosaccu = cosaccu + costerm;
cosctr+=2;
} do {
sinterm = sinterm*(-1)*(radi*radi)/(sinctr*(sinctr + 1));
sinaccu = sinaccu + sinterm;
sinctr+=2;
} while (sinctr <= 2*NUMBER_OF_TERMS);
printf("%.2lf %.12lf %.12lf %.12lf\n", ctr, radi, cosaccu, sinaccu);
} return 0;
}
The code above is accurate for a 15 terms expansion approximation. however, if I change NUMBER_OF_TERMS to, for example, 5 or 10, the approximation is flawed.
Any suggestions?
Let me clarify: I need to obtain an approximation of 5 terms, 10 terms, and 15 terms. I cannot use any other library other than <stdio.h>. I cannot use any other functions outside of int main() (I apologize for the vagueness of my explanation before).
Please answer with the included corrected code.
The key to high precession, yet simple calculation of sind(degrees) and cosd(degrees) is to reduce the range of degree to 0 to 90 first (or even 0 to 45), using the usual trigonometric adjustments with degree arrangements first.
Reductions:
angle = fmod(angle, 360) // reduce (-360..360) or use a = a - (int)(a/360)
sin(x) = -sin(-x) // reduce to [0..360)
cos(x) = cos(-x) // reduce to [0..360)
sin(x) = -sin(x-180) // reduce to [0..180)
cos(x) = -cos(x-180) // reduce to [0..180)
sin(x) = cos(90-x) // reduce to [0..90)
Further reductions:
For [45-90) use sin(x) = cos(90-x) // reduce to [0..45)
then convert to radians and use Taylor series expansion.
Example
Note: Since code is dealing with double, typically 17 digits of precision, no need to use a course PI approximation.
// #define PI 3.141592653589
#define PI 3.1415926535897932384626433832795
I tried your code; it works fine for me, in that it does what it looks like it's designed to do. Here's a comparison between your code's output for the cosine at 5 and 10 terms and the same approximation as calculated by Mathematica. They agree up to <10^-12, i.e. your outputted precision.:
The only problem I see with your code is that, with the way you designed your loops, you're actually taking into account NUMBER_OF_TERMS + 1 terms if you count the first terms in the expansion (i.e. the constant term for the cosine, the linear term for the sine.) You start with this first term, and then your loop adds another NUMMBER_OF_TERMS terms. If that is not by design, you're actually approximating the functions with higher precision that you are expecting.
By its very definition, a Taylor series is a summation of an infinite series of terms.
Thus, a Taylor finite expansion only is an approximation of the true result: as the number of terms increases, the accuracy of this approximation improves.
If there are enough terms, the approximation error at some point becomes unnoticeable. However, if you try lowering the number of terms, the approximation error increases and can detected.
In your case, the approximation error is below the detection threshold for NUMBER_OF_TERMS= 15, but becomes noticeable when NUMBER_OF_TERMS= 10 or less.
The Taylor expansions of sin(x) and cos(x) takes longer to converge as x increases. But since these are periodic functions, you don't actually need to bother expanding the series for values outside the range 0-90°.
For values of x outside this range, use the following identities:
sin(x) = -sin(x+180°) = -sin(-x) = sin(180°-x)
cos(x) = -cos(x+180°) = cos(-x) = -cos(180°-x)
For example, sin(175°) = sin(5°), cos(-120°) = -cos(60°)
i figured it out with help from another user.
turns out i was calculating terms + 1, making the answer more accurate than intended. after 15 terms the changes are past the 12th decimal point, and therefore did not show on the results.
#include <stdio.h>
#define PI 3.141592653589
#define NUMBER_OF_TERMS 10 // 5 and 15 work as well
int
main()
{
int cosctr, sinctr;
double ctr, radi;
double cosaccu, costerm, sinaccu, sinterm; // accu will be final answer, term will be added to accu
for (ctr = -180; ctr < 185; ctr+=5) { // for loop; ctr initialized at -185 and added to in increments of 5 to produce degrees
radi = ctr * PI/180.0; // calculation for radians (assigned to radi)
cosctr = 1; // initialization for cos counter; must be included in loop to allow correct calculations of cos
cosaccu = 1; // first term is 1
costerm = 1; // base term, to be multiplied with termcalc formula
sinctr = 2; // initialization for sin counter; must be included in loop to allow correct calculations of sin
sinaccu = radi; // first term is x, or degrees in radians (radi)
sinterm = radi; // base term for sin
// cos calculation
while (cosctr < 2*NUMBER_OF_TERMS-1) { // accuracy check, 2* since increments of 2; NOTE: actual values are (1, 3, 5,...)
costerm = costerm*(-1)*(radi*radi)/(cosctr*(cosctr + 1)); // TERMCALC FORMULA; multiplying previous term with formula creates next term
cosaccu = cosaccu + costerm; // addition of new term to previous sum; dependent on accuracy (NUMBER_OF_TERMS)
cosctr+=2;
} do { // sin calculation; identical to cos, albeit with substituted vars
sinterm = sinterm*(-1)*(radi*radi)/(sinctr*(sinctr + 1));
sinaccu = sinaccu + sinterm;
sinctr+=2;
} while (sinctr < 2*NUMBER_OF_TERMS-1); // accuracy check, 2* since increments of 2; NOTE: actual values are (2, 4, 6,...)
printf("%.2lf\t%.12lf\t%.12lf\t%.12lf\n", ctr, radi, cosaccu, sinaccu); // final display; /t used for convenience
} return 0; // finally!!!
}
I'm working on a project for our school and we are required to create a program that computes the approximation of the Taylor Expansion Series of sin x and cos x, only using <stdio.h> and without user-defined functions other than int main(), of all angles from -180 to 180 in increments of +5. the following is my code:
#include <stdio.h>
#define PI 3.141592653589
#define NUMBER_OF_TERMS 10
int
main()
{
int cosctr, sinctr;
double ctr, radi;
double cosaccu, costerm, sinaccu, sinterm;
for (ctr = -180; ctr < 185; ctr = ctr + 5) {
radi = ctr * PI/180.0;
cosctr = 1;
cosaccu = 1;
costerm = 1;
sinctr = 2;
sinaccu = radi;
sinterm = radi;
while (cosctr <= 2*NUMBER_OF_TERMS) {
costerm = costerm*(-1)*(radi*radi)/(cosctr*(cosctr + 1));
cosaccu = cosaccu + costerm;
cosctr+=2;
} do {
sinterm = sinterm*(-1)*(radi*radi)/(sinctr*(sinctr + 1));
sinaccu = sinaccu + sinterm;
sinctr+=2;
} while (sinctr <= 2*NUMBER_OF_TERMS);
printf("%.2lf %.12lf %.12lf %.12lf\n", ctr, radi, cosaccu, sinaccu);
} return 0;
}
The code above is accurate for a 15 terms expansion approximation. however, if I change NUMBER_OF_TERMS to, for example, 5 or 10, the approximation is flawed.
Any suggestions?
Let me clarify: I need to obtain an approximation of 5 terms, 10 terms, and 15 terms. I cannot use any other library other than <stdio.h>. I cannot use any other functions outside of int main() (I apologize for the vagueness of my explanation before).
Please answer with the included corrected code.
The key to high precession, yet simple calculation of sind(degrees) and cosd(degrees) is to reduce the range of degree to 0 to 90 first (or even 0 to 45), using the usual trigonometric adjustments with degree arrangements first.
Reductions:
angle = fmod(angle, 360) // reduce (-360..360) or use a = a - (int)(a/360)
sin(x) = -sin(-x) // reduce to [0..360)
cos(x) = cos(-x) // reduce to [0..360)
sin(x) = -sin(x-180) // reduce to [0..180)
cos(x) = -cos(x-180) // reduce to [0..180)
sin(x) = cos(90-x) // reduce to [0..90)
Further reductions:
For [45-90) use sin(x) = cos(90-x) // reduce to [0..45)
then convert to radians and use Taylor series expansion.
Example
Note: Since code is dealing with double, typically 17 digits of precision, no need to use a course PI approximation.
// #define PI 3.141592653589
#define PI 3.1415926535897932384626433832795
I tried your code; it works fine for me, in that it does what it looks like it's designed to do. Here's a comparison between your code's output for the cosine at 5 and 10 terms and the same approximation as calculated by Mathematica. They agree up to <10^-12, i.e. your outputted precision.:
The only problem I see with your code is that, with the way you designed your loops, you're actually taking into account NUMBER_OF_TERMS + 1 terms if you count the first terms in the expansion (i.e. the constant term for the cosine, the linear term for the sine.) You start with this first term, and then your loop adds another NUMMBER_OF_TERMS terms. If that is not by design, you're actually approximating the functions with higher precision that you are expecting.
By its very definition, a Taylor series is a summation of an infinite series of terms.
Thus, a Taylor finite expansion only is an approximation of the true result: as the number of terms increases, the accuracy of this approximation improves.
If there are enough terms, the approximation error at some point becomes unnoticeable. However, if you try lowering the number of terms, the approximation error increases and can detected.
In your case, the approximation error is below the detection threshold for NUMBER_OF_TERMS= 15, but becomes noticeable when NUMBER_OF_TERMS= 10 or less.
The Taylor expansions of sin(x) and cos(x) takes longer to converge as x increases. But since these are periodic functions, you don't actually need to bother expanding the series for values outside the range 0-90°.
For values of x outside this range, use the following identities:
sin(x) = -sin(x+180°) = -sin(-x) = sin(180°-x)
cos(x) = -cos(x+180°) = cos(-x) = -cos(180°-x)
For example, sin(175°) = sin(5°), cos(-120°) = -cos(60°)
i figured it out with help from another user.
turns out i was calculating terms + 1, making the answer more accurate than intended. after 15 terms the changes are past the 12th decimal point, and therefore did not show on the results.
#include <stdio.h>
#define PI 3.141592653589
#define NUMBER_OF_TERMS 10 // 5 and 15 work as well
int
main()
{
int cosctr, sinctr;
double ctr, radi;
double cosaccu, costerm, sinaccu, sinterm; // accu will be final answer, term will be added to accu
for (ctr = -180; ctr < 185; ctr+=5) { // for loop; ctr initialized at -185 and added to in increments of 5 to produce degrees
radi = ctr * PI/180.0; // calculation for radians (assigned to radi)
cosctr = 1; // initialization for cos counter; must be included in loop to allow correct calculations of cos
cosaccu = 1; // first term is 1
costerm = 1; // base term, to be multiplied with termcalc formula
sinctr = 2; // initialization for sin counter; must be included in loop to allow correct calculations of sin
sinaccu = radi; // first term is x, or degrees in radians (radi)
sinterm = radi; // base term for sin
// cos calculation
while (cosctr < 2*NUMBER_OF_TERMS-1) { // accuracy check, 2* since increments of 2; NOTE: actual values are (1, 3, 5,...)
costerm = costerm*(-1)*(radi*radi)/(cosctr*(cosctr + 1)); // TERMCALC FORMULA; multiplying previous term with formula creates next term
cosaccu = cosaccu + costerm; // addition of new term to previous sum; dependent on accuracy (NUMBER_OF_TERMS)
cosctr+=2;
} do { // sin calculation; identical to cos, albeit with substituted vars
sinterm = sinterm*(-1)*(radi*radi)/(sinctr*(sinctr + 1));
sinaccu = sinaccu + sinterm;
sinctr+=2;
} while (sinctr < 2*NUMBER_OF_TERMS-1); // accuracy check, 2* since increments of 2; NOTE: actual values are (2, 4, 6,...)
printf("%.2lf\t%.12lf\t%.12lf\t%.12lf\n", ctr, radi, cosaccu, sinaccu); // final display; /t used for convenience
} return 0; // finally!!!
}
Here is the question..
This is what I've done so far,
#include <stdio.h>
#include <math.h>
long int factorial(int m)
{
if (m==0 || m==1) return (1);
else return (m*factorial(m-1));
}
double power(double x,int n)
{
double val=1;
int i;
for (i=1;i<=n;i++)
{
val*=x;
}
return val;
}
double sine(double x)
{
int n;
double val=0;
for (n=0;n<8;n++)
{
double p = power(-1,n);
double px = power(x,2*n+1);
long fac = factorial(2*n+1);
val += p * px / fac;
}
return val;
}
int main()
{
double x;
printf("Enter angles in degrees: ");
scanf("%lf",&x);
printf("\nValue of sine of %.2f is %.2lf\n",x,sine(x * M_PI / 180));
printf("\nValue of sine of %.2f from library function is %.2lf\n",x,sin(x * M_PI / 180));
return 0;
}
The problem is that the program works perfectly fine from 0 to 180 degrees, but beyond that it gives error.. Also when I increase the value of n in for (n=0;n<8;n++) beyond 8, i get significant error.. There is nothing wrong with the algorithm, I've tested it in my calculator, and the program seems to be fine as well.. I think the problem is due to the range of the data type.. what should i correct to get rid of this error?
Thanks..
You are correct that the error is due to the range of the data type. In sine(), you are calculating the factorial of 15, which is a huge number and does not fit in 32 bits (which is presumably what long int is implemented as on your system). To fix this, you could either:
Redefine factorial to return a double.
Rework your code to combine power and factorial into one loop, which alternately multiplies by x, and divides by i. This will be messier-looking but will avoid the possibility of overflowing a double (granted, I don't think that's a problem for your use case).
15! is indeed beyond range that a 32bit integer can hold. I'd use doubles throughout if I were you.
The taylor series for sin(x) converges more slowly for large values of x. For x outside -π,π. I'd add/subtract multiples of 2*π to get as small an x as possible.
You need range reduction. Note that a Taylor series is best near zero and that in the negative range it is the (negative) mirror image of it's positive range. So, in short: reduce the range (by the modula of 2 PI) to wrap it it the range where you have the highest accuracy. The range beyond 1/2 PI is getting less accurate, so you also want to use the formula: sin(1/2 PI + x) = sin(1/2 PI - x). For negative vales use the formula: sin(-x) = -sin(x). Now you only need to evaluate the interval 0 - 1/2 PI while spanning the whole range. Of course for VERY large values accuracy of the modula of 2 PI will suffer.
You may be having a problem with 15!.
I would print out the values for p, px, fac, and the value for the term for each iteration, and check them out.
You're only including 8 terms in an infinite series. If you think about it for a second in terms of a polynomial, you should see that you don't have a good enough fit for the entire curve.
The fact is that you only need to write the function for 0 <= x <=\pi; all other values will follow using these relationships:
sin(-x) = -sin(x)
and
sin(x+\pi;) = -sin(x)
and
sin(x+2n\pi) = sin(x)
I'd recommend that you normalize your input angle using these to make your function work for all angles as written.
There's a lot of inefficiency built into your code (e.g. you keep recalculating factorials that would easily fit in a table lookup; you use power() to oscillate between -1 and +1). But first make it work correctly, then make it faster.