# Thread: program for calculation of the sine of an angle using the sine series

1. thanks a lot to everyone of you for this valuable enlightening.....i was completely baffled by the large values.....

2. Originally Posted by Pulock2009
thanks a lot to everyone of you for this valuable enlightening.....i was completely baffled by the large values.....
Well unfortunately large values are not your only error (see my post) :-(

But anyway, once you've fixed things I'd strongly consider only ever calculating values of sin(0) through to sin(90). Some may see this as an "optimisation" which in a way it is, but the more important thing is that it keeps the error (error in the sense of distance from ideal value) consistent. The two sources of error are the floating point numbers themselves and also the summation. By only calculating sin 0-90 you can derive the other values and have the same magnitude of error (which is desirable) for "related values". So, it's not an optimization in my point of view, its something to improve precision.

Edit: if the (consistency of) margin of error is not important to you, at least consider deriving the sin of the angles 181-360 from the sin of the angles 0-179 basically because this allows you to use more iterations of the series before the factorial overflows.

3. Ok, I revisited this today. I don't think I can improve it any further... (apart from optimisations). The "Expect" column is using sin() from glibc (math.h)

Code:
```      Angle     Expect        n=4        n=5        n=6        n=7        n=8        n=9   Err. n=9
0    0.00000    0.00000    0.00000    0.00000    0.00000    0.00000    0.00000    0.00000000000000
10    0.17365    0.17365    0.17365    0.17365    0.17365    0.17365    0.17365    0.00000000000000
20    0.34202    0.34202    0.34202    0.34202    0.34202    0.34202    0.34202    0.00000000000000
30    0.50000    0.50000    0.50000    0.50000    0.50000    0.50000    0.50000    0.00000000000000
40    0.64279    0.64279    0.64279    0.64279    0.64279    0.64279    0.64279    0.00000000000000
50    0.76604    0.76604    0.76604    0.76604    0.76604    0.76604    0.76604   -0.00000000000000
60    0.86603    0.86603    0.86603    0.86603    0.86603    0.86603    0.86603   -0.00000000000000
70    0.93969    0.93969    0.93969    0.93969    0.93969    0.93969    0.93969    0.00000000000000
80    0.98481    0.98481    0.98481    0.98481    0.98481    0.98481    0.98481   -0.00000000000000
90    1.00000    1.00000    1.00000    1.00000    1.00000    1.00000    1.00000    0.00000000000000
100    0.98481    0.98481    0.98481    0.98481    0.98481    0.98481    0.98481   -0.00000000000000
110    0.93969    0.93969    0.93969    0.93969    0.93969    0.93969    0.93969    0.00000000000000
120    0.86603    0.86603    0.86603    0.86603    0.86603    0.86603    0.86603    0.00000000000000
130    0.76604    0.76604    0.76604    0.76604    0.76604    0.76604    0.76604    0.00000000000000
140    0.64279    0.64279    0.64279    0.64279    0.64279    0.64279    0.64279    0.00000000000000
150    0.50000    0.50000    0.50000    0.50000    0.50000    0.50000    0.50000    0.00000000000000
160    0.34202    0.34202    0.34202    0.34202    0.34202    0.34202    0.34202    0.00000000000000
170    0.17365    0.17365    0.17365    0.17365    0.17365    0.17365    0.17365    0.00000000000000
180   -0.00000    0.00000    0.00000    0.00000    0.00000    0.00000    0.00000    0.00000000000000
190   -0.17365   -0.17365   -0.17365   -0.17365   -0.17365   -0.17365   -0.17365    0.00000000000000
200   -0.34202   -0.34202   -0.34202   -0.34202   -0.34202   -0.34202   -0.34202    0.00000000000000
210   -0.50000   -0.50000   -0.50000   -0.50000   -0.50000   -0.50000   -0.50000    0.00000000000000
220   -0.64279   -0.64279   -0.64279   -0.64279   -0.64279   -0.64279   -0.64279    0.00000000000000
230   -0.76604   -0.76604   -0.76604   -0.76604   -0.76604   -0.76604   -0.76604    0.00000000000000
240   -0.86603   -0.86603   -0.86603   -0.86603   -0.86603   -0.86603   -0.86603    0.00000000000000
250   -0.93969   -0.93969   -0.93969   -0.93969   -0.93969   -0.93969   -0.93969    0.00000000000000
260   -0.98481   -0.98481   -0.98481   -0.98481   -0.98481   -0.98481   -0.98481    0.00000000000000
270   -1.00000   -1.00000   -1.00000   -1.00000   -1.00000   -1.00000   -1.00000    0.00000000000000
280   -0.98481   -0.98481   -0.98481   -0.98481   -0.98481   -0.98481   -0.98481    0.00000000000000
290   -0.93969   -0.93969   -0.93969   -0.93969   -0.93969   -0.93969   -0.93969   -0.00000000000000
300   -0.86603   -0.86603   -0.86603   -0.86603   -0.86603   -0.86603   -0.86603   -0.00000000000000
310   -0.76604   -0.76604   -0.76604   -0.76604   -0.76604   -0.76604   -0.76604   -0.00000000000000
320   -0.64279   -0.64279   -0.64279   -0.64279   -0.64279   -0.64279   -0.64279   -0.00000000000000
330   -0.50000   -0.50000   -0.50000   -0.50000   -0.50000   -0.50000   -0.50000   -0.00000000000000
340   -0.34202   -0.34202   -0.34202   -0.34202   -0.34202   -0.34202   -0.34202   -0.00000000000000
350   -0.17365   -0.17365   -0.17365   -0.17365   -0.17365   -0.17365   -0.17365   -0.00000000000000
360   -0.00000    0.00000    0.00000    0.00000    0.00000    0.00000    0.00000    0.00000000000000```
Edit: Thanks Pulock2009 for an interesting thing to do in my spare time

Edit 2: Some different values for 'n' and the columns now print the difference (error) from the expected value instead
Code:
``` Angle       Expect      Err n=1      Err n=3      Err n=5      Err n=7      Err n=9     Err n=11           |Error|
-360  0.000000000  0.000000000  0.000000000  0.000000000  0.000000000  0.000000000  0.000000000  0.00000000000000
-340  0.342020143 -0.000043062 -0.000000000 -0.000000000  0.000000000  0.000000000  0.000000000  0.00000000000000
-320  0.642787610 -0.001366063 -0.000000108 -0.000000000  0.000000000  0.000000000  0.000000000  0.00000000000000
-300  0.866025404 -0.010224622 -0.000004132 -0.000000000 -0.000000000 -0.000000000 -0.000000000  0.00000000000000
-280  0.984807753 -0.042225583 -0.000054610 -0.000000012 -0.000000000 -0.000000000 -0.000000000  0.00000000000000
-260  0.984807753 -0.042225583 -0.000054610 -0.000000012 -0.000000000 -0.000000000 -0.000000000  0.00000000000000
-240  0.866025404 -0.010224622 -0.000004132 -0.000000000 -0.000000000  0.000000000  0.000000000  0.00000000000000
-220  0.642787610 -0.001366063 -0.000000108 -0.000000000  0.000000000  0.000000000  0.000000000  0.00000000000000
-200  0.342020143 -0.000043062 -0.000000000  0.000000000  0.000000000  0.000000000  0.000000000  0.00000000000000
-180  0.000000000  0.000000000  0.000000000  0.000000000  0.000000000  0.000000000  0.000000000  0.00000000000000
-160  0.342020143 -0.000043062 -0.000000000 -0.000000000  0.000000000  0.000000000  0.000000000  0.00000000000000
-140  0.642787610 -0.001366063 -0.000000108 -0.000000000  0.000000000  0.000000000  0.000000000  0.00000000000000
-120  0.866025404 -0.010224622 -0.000004132 -0.000000000 -0.000000000 -0.000000000 -0.000000000  0.00000000000000
-100  0.984807753 -0.042225583 -0.000054610 -0.000000012 -0.000000000 -0.000000000 -0.000000000  0.00000000000000
-80  0.984807753 -0.042225583 -0.000054610 -0.000000012 -0.000000000 -0.000000000 -0.000000000  0.00000000000000
-60  0.866025404 -0.010224622 -0.000004132 -0.000000000 -0.000000000  0.000000000  0.000000000  0.00000000000000
-40  0.642787610 -0.001366063 -0.000000108 -0.000000000  0.000000000  0.000000000  0.000000000  0.00000000000000
-20  0.342020143 -0.000043062 -0.000000000  0.000000000  0.000000000  0.000000000  0.000000000  0.00000000000000
0  0.000000000  0.000000000  0.000000000  0.000000000  0.000000000  0.000000000  0.000000000  0.00000000000000
20  0.342020143 -0.000043062 -0.000000000 -0.000000000  0.000000000  0.000000000  0.000000000  0.00000000000000
40  0.642787610 -0.001366063 -0.000000108 -0.000000000  0.000000000  0.000000000  0.000000000  0.00000000000000
60  0.866025404 -0.010224622 -0.000004132 -0.000000000 -0.000000000 -0.000000000 -0.000000000  0.00000000000000
80  0.984807753 -0.042225583 -0.000054610 -0.000000012 -0.000000000 -0.000000000 -0.000000000  0.00000000000000
100  0.984807753 -0.042225583 -0.000054610 -0.000000012 -0.000000000 -0.000000000 -0.000000000  0.00000000000000
120  0.866025404 -0.010224622 -0.000004132 -0.000000000 -0.000000000  0.000000000  0.000000000  0.00000000000000
140  0.642787610 -0.001366063 -0.000000108 -0.000000000  0.000000000  0.000000000  0.000000000  0.00000000000000
160  0.342020143 -0.000043062 -0.000000000  0.000000000  0.000000000  0.000000000  0.000000000  0.00000000000000
180 -0.000000000  0.000000000  0.000000000  0.000000000  0.000000000  0.000000000  0.000000000  0.00000000000000
200 -0.342020143  0.000043062  0.000000000  0.000000000  0.000000000  0.000000000  0.000000000  0.00000000000000
220 -0.642787610  0.001366063  0.000000108  0.000000000  0.000000000  0.000000000  0.000000000  0.00000000000000
240 -0.866025404  0.010224622  0.000004132  0.000000000  0.000000000  0.000000000  0.000000000  0.00000000000000
260 -0.984807753  0.042225583  0.000054610  0.000000012  0.000000000  0.000000000  0.000000000  0.00000000000000
280 -0.984807753  0.042225583  0.000054610  0.000000012  0.000000000  0.000000000  0.000000000  0.00000000000000
300 -0.866025404  0.010224622  0.000004132  0.000000000  0.000000000 -0.000000000 -0.000000000  0.00000000000000
320 -0.642787610  0.001366063  0.000000108  0.000000000 -0.000000000 -0.000000000 -0.000000000  0.00000000000000
340 -0.342020143  0.000043062  0.000000000 -0.000000000 -0.000000000 -0.000000000 -0.000000000  0.00000000000000
360 -0.000000000  0.000000000  0.000000000  0.000000000  0.000000000  0.000000000  0.000000000  0.00000000000000```

4. mistake in your fraction expression.

5. Well, this assignment is well and truly over I think, so I'll paste. I can't get better than 15 decimal places of accuracy using this method.

Code:
```#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <assert.h>
#include <limits.h>
#include <float.h>

double rrduce(double x, double *sign);
double mysin(double x);

#define PI_DIV180   0.01745329251994329576923690768489

void testsin(void)
{
int i;
double esign;

printf(" %6s %22s %22s %22s\n", "Angle", "Expect", "Got", "Error");

for (i = 0; i <= 360; i += 1) {
double angle = i/4.0;
double r = mysin(angle);
expected *= esign;

printf(" %6.2f %22.18f", angle, expected);
printf(" %22.18f", r);
printf(" %22.18f", r - expected);
putchar('\n');
}
}

int main(void)
{
testsin();

return 0;
}

/* Range reduce */
double rrduce(double x, double *sign)
{
if (x < 0)
x = -x;         /* sin(-x) == -sin(x) */

x = fmod(x, 360);

if (x > 180) {       /* Mirror values > 180; those below the x-axis */
x = x - 180;
*sign = -1;
} else
*sign = 1;
if (x > 90)         /* Reflect around x = 90 */
x = 180 - x;

return x;
}

{
return x * PI_DIV180;
}

/* Calculate sin(x). 'x' is the angle in **degrees**
*/
double mysin(double x)
{
double sign;
unsigned i;
double term, sum;
double xsq;
static const unsigned nterms = 10; // Max iterations

xsq = x*x;

term = sum = x;

/* Sum successive terms of the sine Taylor series */
for(i = 0; i < nterms; i++) {
//term = - term * x*x / (2*i+2) / (2*i+3);  // Less precise than (1)...
//term = -term * x*x / ((2*i+2) * (2*i+3)); // Less precise than (1)...
//(1) term = -term / ((2*i+2) * (2*i+3)) * x*x;
//(2) term = -term / (10*i + 4*i*i + 6) * x*x;
//(3) term = -term * 1 / (10*i + 4*i*i + 6) * x*x;

//term = -term * 1 / (4*i*i + 10*i + 6) * x*x;    // same as (1)...
//term = 1 / (4*i*i + 10*i + 6) * x*x * (-term);  // a LOT less accurate
//term = -term * x * 1 / (4*i*i + 10*i + 6) * x;  // a bit less accurate

/* Also experimented with calcuating series using fraction and
* 2^(exp); c.f. frexp(). No improvement.
*/

term = -term / (10*i + 4*i*i + 6) * xsq;

#ifdef MYSINEEARLYEXIT              // Makes no difference to final result
if (fabs(term) <= 1e-17) {
break;
}
#endif

sum += term;
}

return sum * sign;
}```

6. Lol does that code come in English? And I think you're right, I've noticed in some code that I've written that doubles will go down to like 15 or 16 decimal places. What about __float128 types?

7. Originally Posted by MutantJohn
Lol does that code come in English? And I think you're right, I've noticed in some code that I've written that doubles will go down to like 15 or 16 decimal places. What about __float128 types?
I can do much better with long double[1], but I didn't want to go down that path mainly because I learned there are better methods for approximating sine :-) Floating point numbers are certainly tricky.

Use rot13.com on the code

[1] Edit: long double for the calcuations within mysin() that is... it still returns a double, just change the doubles to long doubles in mysin() if you want to play

8. Oops. I meant to edit not post a new post

9. Lol I literally forgot the long double type existed. I do know though that long doubles were supposed to use like 80 bits but __float128 was supposed use the full 128 bits.

10. Originally Posted by MutantJohn
Lol I literally forgot the long double type existed. I do know though that long doubles were supposed to use like 80 bits but __float128 was supposed use the full 128 bits.
In the case of microsoft compilers, support for long doubles was dropped during the transition from 16 bit to 32/64 bit compilers. The old 16 compilers, like VC 2.2 or older, support 80 bit long doubles, but 32 bit and later compilers, like VC 4.0 or Visual Studio, treat long doubles the same as regular double, as 64 bit values. There's a call in VS2005 or later to set the internal floating point precision, but the variables are stuck with the 64 bit format unless you write a program in assembly language or use a different compiler tool set.

11. But gcc works "properly" right?

12. Originally Posted by MutantJohn
But gcc works "properly" right?
Well, that's a good question.

6.5.2s10
There are three real floating types, designated as float, double, and long
double.32) The set of values of the type float is a subset of the set of values of the
type double; the set of values of the type double is a subset of the set of values of the
type long double.
I regard a subset not to be a subset if the subset is exactly the same as the superset (wow)

But then Annex E says that the following are the minimum for those 3 types

#define DBL_MAX 1E+37
#define FLT_MAX 1E+37
#define LDBL_MAX 1E+37
And similarly DBL_EPSILON and LDBL_EPSILON appear to be allowed the same minimum

#define DBL_EPSILON 1E-9
#define DBL_MIN 1E-37
#define FLT_EPSILON 1E-5
#define FLT_MIN 1E-37
#define LDBL_EPSILON 1E-9
#define LDBL_MIN 1E-37
Then we get to F.2 Types
— The double type matches the IEC 60559 double format.
— The long double type matches an IEC 60559 extended format,307) else a
non-IEC 60559 extended format, else the IEC 60559 double format.

...
307) ‘‘Extended’’ is IEC 60559’s double-extended data format. Extended refers to both the common 80-bit
and quadruple 128-bit IEC 60559 formats.
So I guess there's nothing really wrong with the Visual C interpretation.

GCC must use 80-bit long doubles though because they're clearly more precise than double. I suppose I could look at float.h but I'm exhausted now!