'How can I properly build a sin lookup table with C?
To save performance on sin calls, and to handle integer angles, which are more portable manipulated and saved, instead of floating points as angles, I am building a sin lookup function, where 4096 units equals 2pi radians. To save memory, I only store the first 1024 sin values, which are equivalent to sin( [0, pi/2) ).
static const float SinTable[1024] = {0, 0.00153398, ..., 0.999995, 0.999999};
To handle angles in the 3rd and 4th quadrant, I simply conditionally negate:
return Angle&2048 ? -UnsignedSin : UnsignedSin;
Where UnsignedSin is the looked up sin value wrapped between [0, 2048). But how can I handle the second and 4th quadrants? How can I properly map the stored sin values of [0, 1) to [1, 0) conditionally by checking if the angle is in the 2nd or 4th quadrants such as with Angle&1024? I tried this but this is not quite right, because the result for 1024 angle is 0.999999 and not 1 which it should be.
const float UnsignedSin = SinTable[(Angle&1024 ? ~A : A)&1023];
The value of 1 is never stored in the sin table, so I assume a 1-SinTable[...] is required? But I cannot get it quite right.
Solution 1:[1]
You should check about CORDIC algorithm that allows you to get sine and cosine functions with full precision with full savings in space for tables (those are employed in trigonometric functions for embedded architectures since long long time). And use fixed point, instead of floating point or just plain integer values (which gives no sub degree precision at all) Let's say you use a 1/64 of degree (or better 1/2^32 of a full 2*PI rotation or one quadrant, would require around two 32 entries tables) fixed point to achieve enough precision. The CORDIC algorithm will permit you to use two simple tables with one entry per bit of precision you are interested in, and easy and quick calculations (only sums and multiplications are done), and will give you full precision in calculations.
Solution 2:[2]
I suggest you to avoid bit manipulation, since in the future you could change float to double. I propose a more portable version of Brendan answer
`define PI_ADDR 2048
float getSine(unsigned int angle) {
angle = angle % (2*PI_ADDR); // Reduce angle to the range of 1 circle
if( angle < PI_ADDR/2) {
return SinTable[angle];
} else if( angle < PI_ADDR) {
return SinTable[PI_ADDR - angle];
} else if( angle < (PI_ADDR*3/2) ) {
return -SinTable[angle-PI_ADDR];
} else {
return -SinTable[2*PI_ADDR -angle];
}
}
About handling negative angles, be portable too:
return (Angle < 0) ? -UnsignedSin : UnsignedSin;
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | |
| Solution 2 | G. C. |
