Fast Cosine Approximation

Started by
4 comments, last by arriu 13 years, 6 months ago
Hey guys,

I'm trying to remove what seems to be a bottle neck in my noise function. I have a call to Math.Cos() which calculates the cosine in double precision but also adds another function call and cast to float. I have seen simple methods of estimating the cosine function in the past (using a parabola I think) but I'm having trouble tracking them down.

Anyways, here is the function I wish to speed up, any help is appreciated:

// cosine interpolation between a and b by a factor of x
public float lerp(float a, float b, float x)
{
float ft = x * 3.1415927f;
float f = (1.0f - (float)Math.Cos(ft)) * 0.5f;
return a * (1.0f - f) + b * f;
}
Advertisement
In the range -pi/2 to pi/2 you can use the approximatoion
cos(x) ~ -(x-pi/2)(x+pi/2)/(pi*pi/4)

(This approximation is different from the Taylor expansion 1-x^2/2+... but has the nice property of having roots at exactly -pi/2 and pi/2)
Thanks! I may end up using that.

Also I managed to dig up the article I was looking for: http://www.devmaster.net/forums/showthread.php?t=5784

Is there any way to remove the abs()? I am working with the compact .net framework which does not inline any function calls. Calling Math.Abs() may make it pointless.

float sine(float x){    const float B = 4/pi;    const float C = -4/(pi*pi);    float y = B * x + C * x * abs(x);    #ifdef EXTRA_PRECISION    //  const float Q = 0.775;        const float P = 0.225;        y = P * (y * abs(y) - y) + y;   // Q * y + P * y * abs(y)    #endif}
This file has several approximations for trigonometric functions. The
header has the restrictions on the domain of these functions.
If you are working with 32 bit floats all the time, you may want to try changing the precision and rounding mode at the start of the application:

_controlfp(_PC_24, _MCW_PC)_controlfp(_RC_NEAR, _MCW_RC)


Every now and then you will have to check a DLL (i.e. Direct3D) didn't change it back:
assert( (_controlfp(0, 0) & _MCW_PC) == _PC_24 );assert( (_controlfp(0, 0) & _MCW_RC) == _RC_NEAR );


By the way, what happens when you just use cosf()?

If precision isn't your concern, try changing your compiler optimizations from Precise to Fast (in MSVC, Alt+F7 -> C++ -> Code Generation -> Floating Point Model)

Cheers
Dark Sylinc

Edit: Oh I see, this seems C#, then you're probably f**ked.
Yeah, I'm stuck with c#. Which is why I do not want to use the Math.Cos class. It's overkill for what I need.

I guess I am going to use this:
        public static float Cos(float x)        {            const float B = 4 / PI;            const float C = -4 / (TwoPi);            x -= PIby2;            return -(B * x + C * x * ((x < 0) ? -x : x));        }        public static float CosLerp(float a, float b, float x)        {            const float B = 4 / PI;            const float C = -4 / (TwoPi);            float ft = x * PI;            //float f = (1.0f - (float)Math.Cos(ft)) * 0.5f;            float c = ft - PIby2;            float f = (1.0f + (B * c + C * c * ((c < 0) ? -c : c))) * 0.5f;            return a * (1.0f - f) + b * f;        }


edit: even without the extra precision this will do nicely



[Edited by - arriu on September 27, 2010 5:30:08 PM]

This topic is closed to new replies.

Advertisement