Generate sine signal in C without using the standard function

后端 未结 11 906
情歌与酒
情歌与酒 2021-02-02 09:41

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

11条回答
  •  故里飘歌
    2021-02-02 09:46

    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.

提交回复
热议问题