This formula is from a friend of mine --- and I fixed it up for him. But I can't seem to figure out of how to get the right sine calculations per angle. Can someone please help me in getting the right commands in the sin part?
Code:
#include<stdio.h>
#define PI 3.141592653589
#define NUMBER_OF_TERMS 10
double factorial(double x)
{
double counter, total;
counter=x;
total=x;
while(counter>1)
{
counter--;
total = total * counter;
}
return total;
}
double power(double x, double y)
{
double counter, j;
counter=0;
j = x;
while (counter<(y-1))
{
counter++;
x = x * j;
}
return x;
}
double cosine_func(double radians)
{
int counter, x;
double cosine;
x=0;
counter=0;
cosine = 0;
while(counter<NUMBER_OF_TERMS-1)
{
counter++;
x=x+2;
if(counter%2 == 0)
{
cosine = cosine - (power(radians, x)/factorial(x));
}
else if(counter%2 != 0)
{
cosine = cosine + (power(radians, x)/factorial(x));
}
}
return cosine;
}
double sine_func(double radians)
{
int counter, x;
double sin;
x=0;
counter=0;
sin = 0;
while(counter<NUMBER_OF_TERMS-2)
{
counter++;
x=x+3;
if(counter%2 != 0)
{
sin= sin -(power(radians, x)/factorial(x));
}
else if(counter%2 != 0)
{
sin= sin + (power(radians, x)/factorial(x));
}
}
return sin;
}
main()
{
double counter;
double x_angles;
double x_radians;
double cosine;
double sin;
printf("11526769\n\n");
printf("x(degrees)\t x(radians)\t\t cos x\t\t sin x\t\t");
counter = 0;
x_angles = -185;
while (x_angles<180)
{
counter++;
x_angles+=5;
x_radians=x_angles*PI/180.0;
cosine=1-cosine_func(x_radians);
sin=x_radians-sine_func(x_radians);
printf("%.2lf\t\t%.12lf\t\t%.12lf\t\t%.12lf\n", x_angles, x_radians, cosine, sin);
}
return 0;
}
for(f2=3;f2==1;f2--){
fac2*=f2;
}
f2 is initialized with 3 and then in condition portion it is checked that if f2 is equaled to 1 which returns false.
This loop doesn't run for even a single time. Condition is false for the first time. So
fac2*=f2;
is never executed.
And you did same error for every next loop.
Most of your for loops never run. Check the correct syntax for them. Example:
for(f2=3;f2==1;f2--)
Is never executed because f2 = 3 and 3 != 1
#include<stdio.h>
#define PI 3.141592653589
#define NUMBER_OF_TERMS 10
int
main()
{
double angle_degrees, angle_radians;
double sin, i, i2, i3, i4 , i5, i6, i7, i8, i9, i10;
double fac1, fac2, fac3, fac4, fac5, fac6=1, fac7, fac8, fac9, fac10;
double f1, f2, f3, f4, f5, f6, f7, f8, f9, f10;
double p1, p2, p3, p4, p5, p6, p7, p8, p9, p10;
for(angle_degrees = -180; angle_degrees <= 180; angle_degrees += 5){
p1 = angle_radians;
p2 = angle_radians;
p3 = angle_radians;
p4 = angle_radians;
p5 = angle_radians;
p6 = angle_radians;
p7 = angle_radians;
p8 = angle_radians;
p9 = angle_radians;
p10 = angle_radians;
angle_radians = angle_degrees*PI/180;
for(f1=3;f1>=1;f1--){
fac1*=f1;
}
for(f2=5;f2>=1;f2--){
fac1*=f2;
}
for(f3=7;f3>=1;f3--){
fac1*=f3;
}
for(f4=9;f4>=1;f4--){
fac1*=f4;
}
for(f5=11;f5>=1;f5--){
fac1*=f5;
}
for(f6=13;f6>=1;f6--){
fac1*=f6;
}
for(f7=15;f7>=1;f7--){
fac1*=f7;
}
for(f8=17;f8>=1;f8--){
fac1*=f8;
}
for(f9=19;f9>=1;f9--){
fac1*=f9;
}
for(f10=21;f10>=1;f10--){
fac1*=f10;
}
sin = angle_radians - (pow(p1, 3)/fac1) + (pow(p2, 5)/fac2) - (pow(p3, 7)/fac3) + (pow(p4, 9)/fac4) - (pow(p5, 11)/fac5) + (pow(p6, 13)/fac6) - (pow(p7, 15)/fac7) + (pow(p8, 17)/fac8) -(pow(p9, 19)/fac9) - (pow(p10, 21)/fac10);
printf ("%.2lf\t\t%.12lf\t\t%.12lf\n",
angle_degrees, angle_radians, sin);
}
}
Here's a brand new one --- also I forgot these restrictions involved with it:
1)NO other function aside from main().
2)NO inputs involved.
3) Angles = double data.
4)Output = angles (both degrees and radians), cosine (will work on that once done solving for sine), and sine.
The Taylor series of sin(x) is x-x^3/3!+x^5/5!-... . In your code I assume that p1 should mean x^1=x, p2=x^3, .. and f1=1! f2=3!, .. . Now in the result line you write
sin = angle_radians - (p1/fac1) + (p2/fac2) + //...
which has the series with wrong sign + the first term again.
sin = (p1/fac1) - (p2/fac2) + //...
Also the code is overly complicated. You can collect the result in a single loop:
double p=x;
double f=1;
double res=0;
int k=1;
for (int i=0;i<n;i++){
res+=p/f;
p*=-x*x;
k+=2;
f*=(k-1)*k;
}
Here x is the angle in radians p is the current power with right sign and f the factorial of the term.
As commented by LutzL this would overflow at some point because of the factorial. This version is safer against overflows
double res=0;
double term=x;
int k=1;
for (int i=0;i<n;i++){
res+=term;
k+=2;
term*=-x*x/k/(k-1);
}
Code allowing for arbitrary number of terms
Copying myself from https://stackoverflow.com/a/28227419/3088138
Avoiding the recomputation of recursive functions like powers of the same argument and factorials is always a good idea. Thus a reduction to (a minimal amount of) elementary arithmetic operations looks like this:
public static double sine(int terms, double x) {
double result = 1;
double mxx = -x*x;
double addens = 1;
double temp = 2;
for(int n = 2; n <= terms; n++) {
addens *= mxx/temp++/temp++;
result += addens;
}
return x*result;
}
To study a production quality implementation, you can visit the math library libmath of the basic calculator bc, the gnu version can be read at http://code.metager.de/source/xref/gnu/bc/1.06/bc/libmath.b
It normalizes the argument wrt. pi and then uses the Taylor series of sin.
Horner-like schema for fixed number of terms
If you are restricted to a fixed number of terms in the power series, you can use a Horner like code for its evaluation. As example, take 4 terms, then
x-x^3/3!+x^5/5!-x^7/7! = x*(1-x²/6*(1-x²/20*(1-x²/42)))
This can be put into a loop
res = 1
for( k=num_terms; k-->1;) {
res = 1-x*x*res/(2*k*(2*k+1))
}
res = res*x;
or you can unroll the loop
res = 1-x*x/42;
res = 1-x*x/20*res;
res = 1-x*x/6*res;
res = x*res;
You are free to combine x2=x*x or to rename x=angle_radians.
Code can do better than sin(D2R(angle_degrees))
Step 1: Reduce angle to the range 0 to 90 degrees before converting to radians. Take advantage of the usual trig identities. Then we get exact answers for 90*n degrees.
Step 2: Calculate power series from last term up to the first as summing small terms first is more accurate.
#include<stdio.h>
#define PI 3.1415926535897932384626433832795
#define D2R(a) ((a)*PI/180)
#define NUMBER_OF_TERMS 10
int main(void) {
double angle_degrees;
for (angle_degrees = -360; angle_degrees <= 360; angle_degrees += 30) {
double angle_radians = D2R(angle_degrees);
int sign = 1;
double angle_reduced_degrees = fmod(angle_degrees, 360.0);
if (angle_reduced_degrees < 0) {
angle_reduced_degrees = -angle_reduced_degrees;
sign = -1;
}
int quadrant = (int) (angle_reduced_degrees / 90.0);
angle_reduced_degrees -= quadrant * 90;
switch (quadrant) {
case 0:
break;
case 1:
angle_reduced_degrees = 90 - angle_reduced_degrees;
break;
case 2:
angle_reduced_degrees = -angle_reduced_degrees;
sign = -sign;
break;
case 3:
angle_reduced_degrees = 90 - angle_reduced_degrees;
sign = -sign;
break;
}
double angle_reduced_radians = D2R(angle_reduced_degrees);
long long denom = 1;
for (int i = 1; i < NUMBER_OF_TERMS; i++) {
denom *= -(2 * i + 0) * (2 * i + 1);
}
double aa = angle_reduced_radians * angle_reduced_radians;
double sum = 0;
for (int i = NUMBER_OF_TERMS; --i >= 0;) {
double term = 1.0 / (double) denom;
sum = term + sum * aa;
if (i) denom /= (-(2 * i) * (2 * i + 1));
}
sum *= angle_reduced_radians * sign;
sum *= 1.0; // to get rid of -0.0
printf("sine(%+7.2f degrees %+9f radians) = %+20.17f sin()=%+20.17f\n",
angle_degrees, angle_radians, sum, sin(angle_radians));
}
return 0;
}
Output
// This code math.h sin()
sine(-360.00 degrees -6.283185 radians) = -0.00000000000000000 sin()=+0.00000000000000024
sine(-330.00 degrees -5.759587 radians) = +0.50000000000000000 sin()=+0.49999999999999967
sine(-300.00 degrees -5.235988 radians) = +0.86602540378443871 sin()=+0.86602540378443860
sine(-270.00 degrees -4.712389 radians) = +0.99999999999999989 sin()=+1.00000000000000000
sine(-240.00 degrees -4.188790 radians) = -0.86602540378443871 sin()=+0.86602540378443882
sine(-210.00 degrees -3.665191 radians) = -0.50000000000000000 sin()=+0.50000000000000011
sine(-180.00 degrees -3.141593 radians) = -0.00000000000000000 sin()=-0.00000000000000012
sine(-150.00 degrees -2.617994 radians) = -0.50000000000000000 sin()=-0.49999999999999994
sine(-120.00 degrees -2.094395 radians) = -0.86602540378443871 sin()=-0.86602540378443860
sine( -90.00 degrees -1.570796 radians) = -0.99999999999999989 sin()=-1.00000000000000000
sine( -60.00 degrees -1.047198 radians) = -0.86602540378443871 sin()=-0.86602540378443871
sine( -30.00 degrees -0.523599 radians) = -0.50000000000000000 sin()=-0.50000000000000000
sine( +0.00 degrees +0.000000 radians) = +0.00000000000000000 sin()=+0.00000000000000000
sine( +30.00 degrees +0.523599 radians) = +0.50000000000000000 sin()=+0.50000000000000000
sine( +60.00 degrees +1.047198 radians) = +0.86602540378443871 sin()=+0.86602540378443871
sine( +90.00 degrees +1.570796 radians) = +0.99999999999999989 sin()=+1.00000000000000000
sine(+120.00 degrees +2.094395 radians) = +0.86602540378443871 sin()=+0.86602540378443860
sine(+150.00 degrees +2.617994 radians) = +0.50000000000000000 sin()=+0.49999999999999994
sine(+180.00 degrees +3.141593 radians) = +0.00000000000000000 sin()=+0.00000000000000012
sine(+210.00 degrees +3.665191 radians) = +0.50000000000000000 sin()=-0.50000000000000011
sine(+240.00 degrees +4.188790 radians) = +0.86602540378443871 sin()=-0.86602540378443882
sine(+270.00 degrees +4.712389 radians) = -0.99999999999999989 sin()=-1.00000000000000000
sine(+300.00 degrees +5.235988 radians) = -0.86602540378443871 sin()=-0.86602540378443860
sine(+330.00 degrees +5.759587 radians) = -0.50000000000000000 sin()=-0.49999999999999967
sine(+360.00 degrees +6.283185 radians) = +0.00000000000000000 sin()=-0.00000000000000024
Some minor improvements could be made.
Related
#include <stdio.h>
#include <math.h>
int main(void) {
// Set the initial value of pi to 0
double pi = 0.0;
// Set the initial value of the term to 1
double term = 1.0;
// Set the initial value of the divisor to 1
double divisor = 1.0;
// Print the table header
printf("%10s%25s\n", "Number of terms", "Approximation of pi");
// Calculate and print the approximations of pi
for (int i = 1; i <= 20; i++) {
pi += term / divisor;
printf("%10d%25.10f\n", i, pi*4.0);
term *= -1.0;
divisor += 2.0;
}
return 0;
}
I tried to correct the code but still can't get closer to the value as it is ask by my teacher in our assignment...
The Question is..
Calculate the value of π from the infinite series. Print a table that
shows the value of π approximated by one term of this series, by two terms, by three terms,
and so on. How many terms of this series do you have to use before you first get 3.14?
3.141? 3.1415? 3.14159?
How many terms of this series do you have to use before you first get 3.14? 3.141? 3.1415? 3.14159?
The details of "first get 3.14" are a bit unclear. Below attempts something like OP's goal and illustrates the slow convergence as computation time is proportional to the number of terms.
The high number of terms, each incurring round-off errors in the division and addition eventually render this computation too inaccurate for high term count.
int main(void) {
double pi_true = 3.1415926535897932384626433832795;
double threshold = 0.5;
int dp = 0;
// Set the initial value of pi to 0
double pi = 0.0;
// Set the initial value of the term to 1
double term = 1.0;
// Set the initial value of the divisor to 1
double divisor = 1.0;
// Print the table header
printf("%7s %12s %-25.16f\n", "", "", pi_true);
printf("%7s %12s %-25s\n", "", "# of terms", "Approximation of pi");
// Calculate and print the approximations of pi
for (long long i = 1; ; i++) {
pi += term / divisor;
double diff = fabs(4*pi - pi_true);
if (diff <= threshold) {
printf("%7.1e %12lld %-25.16f %-25.*f\n", diff, i, pi * 4.0, dp++, pi * 4.0);
fflush(stdout);
threshold /= 10;
if (4*pi == pi_true) {
break;
}
}
term *= -1.0;
divisor += 2.0;
}
puts("Done");
return 0;
}
Output
3.1415926535897931
# of terms Approximation of pi
4.7e-01 2 2.6666666666666670 3
5.0e-02 20 3.0916238066678399 3.1
5.0e-03 200 3.1365926848388161 3.14
5.0e-04 2000 3.1410926536210413 3.141
5.0e-05 20000 3.1415426535898248 3.1415
5.0e-06 200001 3.1415976535647618 3.14160
5.0e-07 2000001 3.1415931535894743 3.141593
5.0e-08 19999992 3.1415926035897974 3.1415926
5.0e-09 199984633 3.1415926585897931 3.14159266
5.0e-10 1993125509 3.1415926540897927 3.141592654
5.0e-11 19446391919 3.1415926536397927 3.1415926536
...
Ref 3.1415926535897931
On a 2nd attempt, perhaps this is closer to OP's goal
int main(void) {
double pi_true = 3.1415926535897932384626433832795;
double threshold_lo = 2.5;
double threshold_hi = 3.5;
double error_band = 0.5;
int dp = 0;
// Set the initial value of pi to 0
double pi = 0.0;
// Set the initial value of the term to 4
double term = 4.0;
// Set the initial value of the divisor to 1
double divisor = 1.0;
// Print the table header
printf("%12s %-25.16f\n", "", pi_true);
printf("%12s %-25s\n", "# of terms", "Approximation of pi");
// Calculate and print the approximations of pi
for (long long i = 1;; i++) {
pi += term / divisor;
if (pi > threshold_lo && pi < threshold_hi) {
printf("%12lld %-25.16f %-25.*f\n", i, pi, dp++, pi);
fflush(stdout);
char buf[100] = "3.1415926535897932384626433832795";
buf[dp + 2] = 0;
error_band /= 10.0;
double target = atof(buf);
threshold_lo = target - error_band;
threshold_hi = target + error_band;
}
term *= -1.0;
divisor += 2.0;
}
puts("Done");
return 0;
}
Output
3.1415926535897931
# of terms Approximation of pi
2 2.6666666666666670 3
12 3.0584027659273332 3.1
152 3.1350137774059244 3.14
916 3.1405009508583017 3.141
7010 3.1414500002381582 3.1415
130658 3.1415850000208838 3.14159
866860 3.1415915000009238 3.141592
9653464 3.1415925500000141 3.1415926
116423306 3.1415926450000007 3.14159265
919102060 3.1415926525000004 3.141592653
7234029994 3.1415926534500005 3.1415926535
Im writing a C code programm that calcultates sine and cosine of a given angle without using the Sine and Cosine Functions of the Math.h library.
But the problem I am facing right now is that i can only calculate the sine and cosine of the Angles between -90° - 90° (so the angles in the first and fourth quadrant). The Cosine(100) = Cosine(80) with a negative operator. So my way of thinking would be to just write code that whenever it gets an angle that is greater than 90 and smaller than 270, it should just substract the additional value from 90; so in the case of Cos(240) that would be the same as Cos(90-150) with an inverted operator infront.
How should one go about this, without having to write 180-if statements?
#include <stdio.h>
#include <math.h>
int main() {
double alpha[29];
alpha[0] = 45.00000000;
alpha[1] = 26.56505118;
alpha[2] = 14.03624347;
alpha[3] = 7.12501635;
alpha[4] = 3.57633437;
alpha[5] = 1.78991061;
alpha[6] = 0.89517371;
alpha[7] = 0.44761417;
alpha[8] = 0.22381050;
alpha[9] = 0.11190568;
alpha[10] = 0.05595289;
alpha[11] = 0.02797645;
alpha[12] = 0.01398823;
alpha[13] = 0.00699411;
alpha[14] = 0.00349706;
alpha[15] = 0.00174853;
alpha[16] = 0.00087426;
alpha[17] = 0.00043713;
alpha[18] = 0.00021857;
alpha[19] = 0.00010928;
alpha[20] = 0.00005464;
alpha[21] = 0.00002732;
alpha[22] = 0.00001366;
alpha[23] = 0.00000683;
alpha[24] = 0.00000342;
alpha[25] = 0.00000171;
alpha[26] = 0.00000085;
alpha[27] = 0.00000043;
alpha[28] = 0.00000021;
double x = 0.60725294;
double y = 0;
double winkel = -150;
double theta = winkel;
double xs;
double ys;
int i = 0;
}
while ( i < 29 ){
printf("This is run number %d with theta = %lf \n", i, theta);
xs = y / pow(2, i);
ys = x / pow(2, i);
if (theta <= 0){
x = x + xs;
y = y - ys;
theta = theta + alpha[i];
} else {
x = x - xs;
y = y + ys;
theta = theta - alpha[i];
};
printf("x = %lf and y = %lf \n \n",x,y);
i++;
}
printf("cosine = %lf\n", x);
printf("sine = %lf\n", y);
return 0;
}
cos(x) = cos(-x)
cos(x) = cos(x%360) if x is in degrees and x is positive
those identities should be sufficient to understand what to do, right?
likewise sin(-x) = -sin(x)
sin(x) = sin(x%360) if x is in degrees and x is positive
#include <stdio.h>
#include <math.h>
const int TERMS = 7;
const float PI = 3.14159265358979;
int fact(int n) {
return n<= 0 ? 1 : n * fact(n-1);
}
double sine(int x) {
double rad = x * (PI / 180);
double sin = 0;
int n;
for(n = 0; n < TERMS; n++) { // That's Taylor series!!
sin += pow(-1, n) * pow(rad, (2 * n) + 1)/ fact((2 * n) + 1);
}
return sin;
}
double cosine(int x) {
double rad = x * (PI / 180);
double cos = 0;
int n;
for(n = 0; n < TERMS; n++) { // That's also Taylor series!
cos += pow(-1, n) * pow(rad, 2 * n) / fact(2 * n);
}
return cos;
}
int main(void){
int y;
scanf("%d",&y);
printf("sine(%d)= %lf\n",y, sine(y));
printf("cosine(%d)= %lf\n",y, cosine(y));
return 0;
}
The code above was implemented to compute sine and cosine using Taylor series.
I tried testing the code and it works fine for sine(120).
I am getting wrong answers for sine(240) and sine(300).
Can anyone help me find out why those errors occur?
You should calculate the functions in the first quadrant only [0, pi/2). Exploit the properties of the functions to get the values for other angles. For instance, for values of x between [pi/2, pi), sin(x) can be calculated by sin(pi - x).
The sine of 120 degrees, which is 40 past 90 degrees, is the same as 50 degrees: 40 degrees before 90. Sine starts at 0, then rises toward 1 at 90 degrees, and then falls again in a mirror image to zero at 180.
The negative sine values from pi to 2pi are just -sin(x - pi). I'd handle everything by this recursive definition:
sin(x):
cases x of:
[0, pi/2) -> calculate (Taylor or whatever)
[pi/2, pi) -> sin(pi - x)
[pi/2, 2pi) -> -sin(x - pi)
< 0 -> sin(-x)
>= 2pi -> sin(fmod(x, 2pi)) // floating-point remainder
A similar approach for cos, using identity cases appropriate for it.
The key point is:
TERMS is too small to have proper precision. And if you increase TERMS, you have to change fact implementation as it will likely overflow when working with int.
I would use a sign to toggle the -1 power instead of pow(-1,n) overkill.
Then use double for the value of PI to avoid losing too many decimals
Then for high values, you should increase the number of terms (this is the main issue). using long long for your factorial method or you get overflow. I set 10 and get proper results:
#include <stdio.h>
#include <math.h>
const int TERMS = 10;
const double PI = 3.14159265358979;
long long fact(int n) {
return n<= 0 ? 1 : n * fact(n-1);
}
double powd(double x,int n) {
return n<= 0 ? 1 : x * powd(x,n-1);
}
double sine(int x) {
double rad = x * (PI / 180);
double sin = 0;
int n;
int sign = 1;
for(n = 0; n < TERMS; n++) { // That's Taylor series!!
sin += sign * powd(rad, (2 * n) + 1)/ fact((2 * n) + 1);
sign = -sign;
}
return sin;
}
double cosine(int x) {
double rad = x * (PI / 180);
double cos = 0;
int n;
int sign = 1;
for(n = 0; n < TERMS; n++) { // That's also Taylor series!
cos += sign * powd(rad, 2 * n) / fact(2 * n);
sign = -sign;
}
return cos;
}
int main(void){
int y;
scanf("%d",&y);
printf("sine(%d)= %lf\n",y, sine(y));
printf("cosine(%d)= %lf\n",y, cosine(y));
return 0;
}
result:
240
sine(240)= -0.866026
cosine(240)= -0.500001
Notes:
my recusive implementation of pow using successive multiplications is probably not needed, since we're dealing with floating point. It introduces accumulation error if n is big.
fact could be using floating point to allow bigger numbers and better precision. Actually I suggested long long but it would be better not to assume that the size will be enough. Better use standard type like int64_t for that.
fact and pow results could be pre-computed/hardcoded as well. This would save computation time.
const double TERMS = 14;
const double PI = 3.14159265358979;
double fact(double n) {return n <= 0.0 ? 1 : n * fact(n - 1);}
double sine(double x)
{
double rad = x * (PI / 180);
rad = fmod(rad, 2 * PI);
double sin = 0;
for (double n = 0; n < TERMS; n++)
sin += pow(-1, n) * pow(rad, (2 * n) + 1) / fact((2 * n) + 1);
return sin;
}
double cosine(double x)
{
double rad = x * (PI / 180);
rad = fmod(rad,2*PI);
double cos = 0;
for (double n = 0; n < TERMS; n++)
cos += pow(-1, n) * pow(rad, 2 * n) / fact(2 * n);
return cos;
}
int main()
{
printf("sine(240)= %lf\n", sine(240));
printf("cosine(300)= %lf\n",cosine(300));
}
I've been working on a program that calculates sin(x), cos(x), and exp(x) without using math.h and compares them to the library values of their functions. I've been forbidden from actually using the basic power(x, n) and fact(n) functions. The only hint is that I have to do division before doing multiplication when combining the functions into one.
double power(double x, int n)
{
int i;
double prod=1.;
for(i=0;i<n;i++){
prod = prod*x;
}
return prod;
}
double fact(int n)
{
int i;
double prod=1.;
for(i=1;i<=n;i++) {
prod = prod*i;
}
return prod;
}
My idea is to somehow nest the for-loops together, and piecemeal the Taylor Expansion formula for each iteration of the loop, but I haven't had luck actually combining the two.
Any help or hint would be appreciated on how to combine these.
The other aspect of the program that confuses me is that there can only be a single input of X per iteration of the program, and therefore no dynamically defined 'n' for the loops.
Use Taylor series for the exponential:
e^x = 1 + x/1! + x^2/2! + x^3/3!...
and using Euler after that you can calculate sinx and cosx.
The trick is to look at the changes between each successive term in the Taylor series expansion. Let's start with ex:
e^x = 1 + x + x^2/2! + x^3/3! + x^4/4! + x^5/5! ...
Notice that each term is x / n times the prior term, where n is the term number. So start with a term of 1, then multiply by the above expression to get the next term.
That gives you the following implementation:
double etox(double x)
{
long double sum = 0;
// term starts at 1
long double term = 1;
// term number
int i = 1;
// continue until the term is below the precision of the current sum
while (sum + term != sum) {
sum += term;
// new term is x/i times the prior term, where i is the term number
term *= (long double)x / i;
i++;
}
return sum;
}
Note that with this implementation, you'll get some degree of error in the least significant digits. If you start adding from a higher term number and work your way back, this can be avoided.
Similarly for sin(x) and cos(x):
sin(x) = x - x^3/3! + x^5/5! - x^7/7! + x^9/9! ...
cos(x) = 1 - x^2/2! + x^4/4! - x^6/6! + x^8/8! ...
Each term is - (x*x) / ((2*n)*((2*n)-1)) times the prior term, where n is the term number.
I'll leave the the implementation of these two as an exercise for the reader.
Part of this comes from my answer for doing this in MIPS assembly: Taylor Series in MIPS assembly
You can do Taylor series on the fly without having to call sub-functions. In the series, each term can be calculated from the previous term in a loop. (i.e. no need to call fact and/or pow repeatedly, where each starts from the beginning). See https://en.wikipedia.org/wiki/Taylor_series
Anyway, here's code for sin and cos:
// mipstaylor/mipstaylor -- fast sine/cosine calculation
#include <stdio.h>
#include <math.h>
#define ITERMAX 10
// qcos -- calculate cosine
double
qcos(double x)
{
int iteridx;
double x2;
double cur;
int neg;
double xpow;
double n2m1;
double nfac;
double sum;
// square of x
x2 = x * x;
// values for initial terms where n==0:
xpow = 1.0;
n2m1 = 0.0;
nfac = 1.0;
neg = 1;
sum = 0.0;
iteridx = 0;
// NOTES:
// (1) with the setup above, we can just use the loop without any special
// casing
while (1) {
// calculate current value
cur = xpow / nfac;
// apply it to sum
if (neg < 0)
sum -= cur;
else
sum += cur;
// bug out when done
if (++iteridx >= ITERMAX)
break;
// now calculate intermediate values for _next_ sum term
// get _next_ power term
xpow *= x2;
// go from factorial(2n) to factorial(2n+1)
n2m1 += 1.0;
nfac *= n2m1;
// now get factorial(2n+1+1)
n2m1 += 1.0;
nfac *= n2m1;
// flip sign
neg = -neg;
}
return sum;
}
// qsin -- calculate sine
double
qsin(double x)
{
int iteridx;
double x2;
double cur;
int neg;
double xpow;
double n2m1;
double nfac;
double sum;
// square of x
x2 = x * x;
// values for initial terms where n==0:
xpow = x;
n2m1 = 1.0;
nfac = 1.0;
neg = 1;
sum = 0.0;
iteridx = 0;
// NOTES:
// (1) with the setup above, we can just use the loop without any special
// casing
while (1) {
// calculate current value
cur = xpow / nfac;
// apply it to sum
if (neg < 0)
sum -= cur;
else
sum += cur;
// bug out when done
if (++iteridx >= ITERMAX)
break;
// now calculate intermediate values for _next_ sum term
// get _next_ power term
xpow *= x2;
// go from factorial(2n+1) to factorial(2n+1+1)
n2m1 += 1.0;
nfac *= n2m1;
// now get factorial(2n+1+1+1)
n2m1 += 1.0;
nfac *= n2m1;
// flip sign
neg = -neg;
}
return sum;
}
// testfnc -- test function
void
testfnc(int typ,const char *sym)
{
double (*efnc)(double);
double (*qfnc)(double);
double vale;
double valq;
double x;
double dif;
int iter;
switch (typ) {
case 0:
efnc = cos;
qfnc = qcos;
break;
case 1:
efnc = sin;
qfnc = qsin;
break;
default:
efnc = NULL;
qfnc = NULL;
break;
}
iter = 0;
for (x = 0.0; x <= M_PI_2; x += 0.001, ++iter) {
vale = efnc(x);
valq = qfnc(x);
dif = vale - valq;
dif = fabs(dif);
printf("%s: %d x=%.15f e=%.15f q=%.15f dif=%.15f %s\n",
sym,iter,x,vale,valq,dif,(dif < 1e-14) ? "PASS" : "FAIL");
}
}
// main -- main program
int
main(int argc,char **argv)
{
testfnc(0,"cos");
testfnc(1,"sin");
return 0;
}
i posted this before, user told me to post it on codereview. i did, and they closed it...so one more time here: (i deleted the old question)
I have these formulas:
and I need the Poisson formulas for the erlangC formula:
I tried to rebuild the formulas in C:
double getPoisson(double m, double u, bool cumu)
{
double ret = 0;
if(!cumu)
{
ret = (exp(-u)*pow(u,m)) / (factorial(m));
}
else
{
double facto = 1;
double ehu = exp(-u);
for(int i = 0; i < m; i++)
{
ret = ret + (ehu * pow(u,i)) / facto;
facto *= (i+1);
}
}
return ret;
}
The Erlang C Formula:
double getErlangC(double m, double u, double p)
{
double numerator = getPoisson(m, u, false);
double denominator = getPoisson(m, u, false) + (1-p) * getPoisson(m, u, true);
return numerator/denominator;
}
The main problem is, the m parameter in getPoisson is a big value (>170)
so it wants to calculate >170! but it cannot handle it. I think the primitive data types are too small to work correctly, or what do you say?
BTW: This is the factorial function I use for the first Poisson:
double factorial(double n)
{
if(n >= 1)
return n*factorial(n-1);
else
return 1;
}
Some samples:
Input:
double l = getErlangC(50, 48, 0.96);
printf("%g", l);
Output:
0.694456 (correct)
Input:
double l = getErlangC(100, 96, 0.96);
printf("%g", l);
Output:
0.5872811 (correct)
if i use a value higher than 170 for the first parameter (m) of getErlangC like:
Input:
double l = getErlangC(500, 487, 0.974);
printf("%g", l);
Output:
naN (incorrect)
Excepted:
0.45269
How's my approach? Would be there a better way to calculate Poisson and erlangC?
Some Info: Excel has the POISSON Function, and on Excel it works perfekt... would there be a way to see the algorithm(code) EXCEL uses for POISSON?
(pow(u, m)/factorial(m)) can be expressed as a recursive loop with each element shown as u/n where each n is an element of m!.
double ratio(double u, int n)
{
if(n > 0)
{
// Avoid the ratio overflow by calculating each ratio element
double val;
val = u/n;
return val*ratio(u, n-1);
}
else
{
// Avoid division by 0 as power and factorial of 0 are 1
return 1;
}
}
Note that if you want to avoid recursion, you can do it as a loop as well
double ratio(double u, int n)
{
int i;
// Avoid the ratio overflow by calculating each ratio element
// default the ratio to 1 for n == 0
double val = 1;
// calculate the next n-1 ratios and put them into the total
for (i = 1; i<=n; i++)
{
// Put in the next element of the ratio
val *= u/i;
}
// return the final value of the ratio
return val;
}
To cope with values exceeding the double range, re-code to use the log of values. Downside- some precision loss.
Precision can be re-gained with improved code, but here is something that at least copes with the range issues.
Slight variant of OP's code follows: Used for comparison.
long double factorial(unsigned m) {
long double f = 1.0;
while (m > 0) {
f *= m;
m--;
}
return f;
}
double getPoisson(unsigned m, double u, bool cumu) {
double ret = 0;
if (!cumu) {
ret = (double) ((exp(-u) * pow(u, m)) / (factorial(m)));
} else {
double facto = 1;
double ehu = exp(-u);
for (unsigned i = 0; i < m; i++) {
ret = ret + (ehu * pow(u, i)) / facto;
facto *= (i + 1);
}
}
return ret;
}
double getErlang(unsigned m, double u, double p) {
double numerator = getPoisson(m, u, false);
double denominator = numerator + (1.0 - p) * getPoisson(m, u, true);
return numerator / denominator;
}
Suggested changes
#ifdef M_PI
#define MY_PI M_PI
#else
#define MY_PI 3.1415926535897932384626433832795
#endif
// log of n!
//
// Gosper Approximation of Stirling's Approximation
// http://mathworld.wolfram.com/StirlingsApproximation.html
// n! about= sqrt(pi*(2*n + 1/3.)) * pow(n,n) * exp(-n)
static double ln_factorial(unsigned n) {
if (n <= 1) return 0.0;
double x = n;
return log(sqrt(MY_PI * (2 * x + 1 / 3.0))) + log(x) * x - x;
}
double getPoisson_2(unsigned m, double u, bool cumu) {
double ret = 0.0;
if (cumu) {
// Simplify term calculation. `mul` does not get too large nor small.
double mul = exp(-u);
for (unsigned i = 0; i < m; i++) {
ret += mul;
mul *= u/(i + 1);
// printf("ret:% 10e mul:% 10e\n", ret, mul);
}
} else {
// ret = (exp(-u) * pow(u, m)) / (factorial(m));
double ln_ret = -u + log(u) * m - ln_factorial(m);
return exp(ln_ret);
}
return ret;
}
double getErlang_2(unsigned m, double u, double p) {
double numerator = getPoisson_2(m, u, false);
double denominator = numerator + (1 - p) * getPoisson_2(m, u, true);
return numerator / denominator;
}
Test code
void ErTest(unsigned m, double u, double p, double expect) {
printf("m:%4u u:% 14e p:% 14e", m, u, p);
printf(" E0:% 14e", expect);
double y1 = getErlang(m, u, p);
printf(" E1:% 14e", y1);
double y2 = getErlang_2(m, u, p);
printf(" E2:% 14e", y2);
puts("");
}
int main(void) {
ErTest(50, 48, 0.96, 0.694456);
ErTest(100, 96, 0.96, 0.5872811);
ErTest(500, 487, 0.974, 0.45269);
}
m: 50 u: 4.800000e+01 p: 9.600000e-01 E0: 6.944560e-01 E1: 6.944556e-01 E2: 6.944562e-01
m: 100 u: 9.600000e+01 p: 9.600000e-01 E0: 5.872811e-01 E1: 5.872811e-01 E2: 5.872813e-01
m: 500 u: 4.870000e+02 p: 9.740000e-01 E0: 4.526900e-01 E1: nan E2: 4.464746e-01
Your large recursive factorial is a problem as it might produce a stack overflow as well as a value overflow. pow might also get large.
Here's a way to combine things incrementally:
double
getPoisson(double m, double u, bool cumu)
{
double sum = 0;
double facto = 1;
double u_i = 1;
double ehu = exp(-u);
double cur = ehu;
// u_i -- pow(u,i)
// cur -- current/last term in series
// sum -- sum of terms
for (int i = 0; i < m; i++) {
cur = (ehu * u_i) / facto;
sum += cur;
u_i *= u;
facto *= (i + 1);
}
return cumu ? sum : cur;
}
The above is "okay", but still might overflow some values because of the u_i and facto terms.
Here is an alternate that combines the terms as a ratio. It is less likely to overflow:
double
getPoisson(double m, double u, bool cumu)
{
double sum = 0;
double ehu = exp(-u);
double cur = ehu;
double ratio = 1;
// cur -- current/last term in series
// sum -- sum of terms
// ratio -- u^i / factorial(i)
for (int i = 0; i < m; i++) {
cur = ehu * ratio;
sum += cur;
ratio *= u;
ratio /= (i + 1);
}
return cumu ? sum : cur;
}
The above might still produce some large values. If so, you might have to use long double, quadmath, or multiprecision arithmetic. Or, come up with an "analog" of the equation/algorithm.