Sign in to follow this  

Fast Cosine Approximation

This topic is 2635 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

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;
}

Share this post


Link to post
Share on other sites
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)

Share this post


Link to post
Share on other sites
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
}

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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]

Share this post


Link to post
Share on other sites

This topic is 2635 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this