I want to generate a sine signal in C without using the standard function sin() in order to trigger sine shaped changes in the brightness of a LED. My basic idea was to use a lo
You could use the first few terms of the Taylor series expansion of sin
. You can use as few terms as needed to reach your intended level of precision - a few more terms than the below example should start to bump up against the limits of a 32-bit float.
Example:
#include
// Please use the built-in floor function if you can.
float my_floor(float f) {
return (float) (int) f;
}
// Please use the built-in fmod function if you can.
float my_fmod(float f, float n) {
return f - n * my_floor(f / n);
}
// t should be in given in radians.
float sin_t(float t) {
const float PI = 3.14159265359f;
// First we clamp t to the interval [0, 2*pi)
// because this approximation loses precision for
// values of t not close to 0. We do this by
// taking fmod(t, 2*pi) because sin is a periodic
// function with period 2*pi.
t = my_fmod(t, 2.0f * PI);
// Next we clamp to [-pi, pi] to get our t as
// close to 0 as possible. We "reflect" any values
// greater than pi by subtracting them from pi. This
// works because sin is an odd function and so
// sin(-t) = -sin(t), and the particular shape of sin
// combined with the choice of pi as the endpoint
// takes care of the negative.
if (t >= PI) {
t = PI - t;
}
// You can precompute these if you want, but
// the compiler will probably optimize them out.
// These are the reciprocals of odd factorials.
// (1/n! for odd n)
const float c0 = 1.0f;
const float c1 = c0 / (2.0f * 3.0f);
const float c2 = c1 / (4.0f * 5.0f);
const float c3 = c2 / (6.0f * 7.0f);
const float c4 = c3 / (8.0f * 9.0f);
const float c5 = c4 / (10.0f * 11.0f);
const float c6 = c5 / (12.0f * 13.0f);
const float c7 = c6 / (14.0f * 15.0f);
const float c8 = c7 / (16.0f * 17.0f);
// Increasing odd powers of t.
const float t3 = t * t * t;
const float t5 = t3 * t * t;
const float t7 = t5 * t * t;
const float t9 = t7 * t * t;
const float t11 = t9 * t * t;
const float t13 = t9 * t * t;
const float t15 = t9 * t * t;
const float t17 = t9 * t * t;
return c0 * t - c1 * t3 + c2 * t5 - c3 * t7 + c4 * t9 - c5 * t11 + c6 * t13 - c7 * t15 + c8 * t17;
}
// Test the output
int main() {
const float PI = 3.14159265359f;
float t;
for (t = 0.0f; t < 12.0f * PI; t += (PI * 0.25f)) {
printf("sin(%f) = %f\n", t, sin_t(t));
}
return 0;
}
Example output:
sin(0.000000) = 0.000000
sin(0.785398) = 0.707107
sin(1.570796) = 1.000000
sin(2.356194) = 0.707098
sin(3.141593) = 0.000000
sin(3.926991) = -0.707107
sin(4.712389) = -1.000000
sin(5.497787) = -0.707098
sin(6.283185) = 0.000398
...
sin(31.415936) = 0.000008
sin(32.201332) = 0.707111
sin(32.986729) = 1.000000
sin(33.772125) = 0.707096
sin(34.557522) = -0.000001
sin(35.342918) = -0.707106
sin(36.128315) = -1.000000
sin(36.913712) = -0.707100
sin(37.699108) = 0.000393
As you can see there is still some room for improvement in precision. I am not a genius with floating point arithmetic so probably some of it has to do with the floor
/fmod
implementations or the specific order the mathematical operations are performed in.