sin(pi)
does not return 0 because pi
is not π.
π is an irrational number. Even with extended precision and the best acos()/sin()
routines, the value of π can only be approximated. The value returned from acos(-1)
is machine-pi, the closest double
to π.
printf("machine pi %.20f...\n", pi);
// machine pi 3.14159265358979311600...
// math pi 3.14159265358979323846...
// ^^^^^...
When code calculates pi * (2.0*64/128.0)
, the product can be expected to be exact with no rounding error, yet the value of pi
here is machine-pi and not π. The sine of machine-pi is indeed about 1.22...e-16
I am porting floating-point to fixed-point code and this small difference escalates.
For trigonometric functions, if the original argument is degrees (or in OP's case 1/128th of a circle) or some exact period, unlike the irrational 2*π radians: perform the range reductions first in original units, then perhaps then use trig identities.
Lastly convert to radians and apply the trig function.
#include <math.h>
#include <stdio.h>
#ifndef M_PI
#define M_PI 3.1415926535897932384626433832795
#endif
// Binary angle measurement
#define BAM 128
static double bam128tor(int bam) {
return M_PI * 2 / BAM * bam;
}
double sin_BAM128(int bam) {
// mod by the integral number of units in a circle
bam %= BAM;
if (bam < 0) bam += BAM;
unsigned quadrant = 0;
if (bam >= BAM / 2) {
bam -= BAM / 2;
quadrant += 2;
}
if (bam >= BAM / 4) {
bam -= BAM / 4;
quadrant += 1;
}
if (bam >= BAM / 8) {
bam = -(BAM / 4 - bam);
quadrant = (quadrant + 1) % 4;
}
printf("%2u %4d ", quadrant, bam);
switch (quadrant % 4) {
case 0:
return sin(bam128tor(bam));
case 1:
return cos(bam128tor(bam));
case 2:
return sin(bam128tor(-bam)) * 1.0;
default:
return -cos(bam128tor(bam));
}
}
int main(void) {
double pi = acos(-1);
for (int i = 0; i <= BAM; i += 4) {
double resultD = sin(pi * (2.0 * i / 128.0));
//float resultF = sin(pi * (2.0f * i / 128.0f));
printf("sine[%3d]\t% .20f\t% .20f\n", i, resultD, sin_BAM128(i));
}
}
Output
0 0 sine[ 0] 0.00000000000000000000 0.00000000000000000000
0 4 sine[ 4] 0.19509032201612824808 0.19509032201612824808
0 8 sine[ 8] 0.38268343236508978178 0.38268343236508978178
0 12 sine[ 12] 0.55557023301960217765 0.55557023301960217765
1 -16 sine[ 16] 0.70710678118654746172 0.70710678118654757274
1 -12 sine[ 20] 0.83146961230254523567 0.83146961230254523567
1 -8 sine[ 24] 0.92387953251128673848 0.92387953251128673848
1 -4 sine[ 28] 0.98078528040323043058 0.98078528040323043058
1 0 sine[ 32] 1.00000000000000000000 1.00000000000000000000
1 4 sine[ 36] 0.98078528040323043058 0.98078528040323043058
1 8 sine[ 40] 0.92387953251128673848 0.92387953251128673848
1 12 sine[ 44] 0.83146961230254545772 0.83146961230254523567
2 -16 sine[ 48] 0.70710678118654757274 0.70710678118654746172
2 -12 sine[ 52] 0.55557023301960206663 0.55557023301960217765
2 -8 sine[ 56] 0.38268343236508989280 0.38268343236508978178
2 -4 sine[ 60] 0.19509032201612860891 0.19509032201612824808
2 0 sine[ 64] 0.00000000000000012246 0.00000000000000000000 exact
2 4 sine[ 68] -0.19509032201612835911 -0.19509032201612824808
2 8 sine[ 72] -0.38268343236508967076 -0.38268343236508978178
2 12 sine[ 76] -0.55557023301960195560 -0.55557023301960217765
3 -16 sine[ 80] -0.70710678118654746172 -0.70710678118654757274
3 -12 sine[ 84] -0.83146961230254523567 -0.83146961230254523567
3 -8 sine[ 88] -0.92387953251128651644 -0.92387953251128673848
3 -4 sine[ 92] -0.98078528040323031956 -0.98078528040323043058
3 0 sine[ 96] -1.00000000000000000000 -1.00000000000000000000
3 4 sine[100] -0.98078528040323043058 -0.98078528040323043058
3 8 sine[104] -0.92387953251128662746 -0.92387953251128673848
3 12 sine[108] -0.83146961230254545772 -0.83146961230254523567
0 -16 sine[112] -0.70710678118654768376 -0.70710678118654746172
0 -12 sine[116] -0.55557023301960217765 -0.55557023301960217765
0 -8 sine[120] -0.38268343236509039240 -0.38268343236508978178
0 -4 sine[124] -0.19509032201612871993 -0.19509032201612824808
0 0 sine[128] -0.00000000000000024493 0.00000000000000000000 exact