Skip to main content
gamedev.net
🔒 Locked

Fast Cosine Approximation

Started by arriu Sep 27, 2010 at 2:11 PM 4 replies 3.6k views
Original Post
arriu
arriu
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;
}
Kambiz
Kambiz
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)
arriu
arriu
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}
Dave Eberly
Dave Eberly
This file has several approximations for trigonometric functions. The
header has the restrictions on the domain of these functions.
Matias Goldberg
Matias Goldberg
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.
arriu
arriu
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]

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.