Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

sin and cos are slow, is there an alternatve?

Tags:

c++

math

My game needs to move by a certain angle. To do this I get the vector of the angle via sin and cos. Unfortunately sin and cos are my bottleneck. I'm sure I do not need this much precision. Is there an alternative to a C sin & cos and look-up table that is decently precise but very fast?

I had found this:

float Skeleton::fastSin( float x )
{
    const float B = 4.0f/pi;
    const float C = -4.0f/(pi*pi);

    float y = B * x + C * x * abs(x);

    const float P = 0.225f;

    return P * (y * abs(y) - y) + y; 
}

Unfortunately, this does not seem to work. I get significantly different behavior when I use this sin rather than C sin.

Thanks

like image 824
jmasterx Avatar asked May 23 '11 00:05

jmasterx


People also ask

Are sin and cos ever equal?

Solution: In order for the sine and cosine to be equal, the angles must be complementary. cos (15º) = 0.97, find sin(75º) and the cos(75º). Solution: The sine of an angle and the cosine of its complement are equal.

Which is better sine or cosine?

The cosine is nicer because it corresponds to the real part of the exponential of imaginary numbers and its power series is invertible.

What does sin and cos actually do?

Sine and cosine — a.k.a., sin(θ) and cos(θ) — are functions revealing the shape of a right triangle. Looking out from a vertex with angle θ, sin(θ) is the ratio of the opposite side to the hypotenuse , while cos(θ) is the ratio of the adjacent side to the hypotenuse .


2 Answers

A lookup table is the standard solution. You could Also use two lookup tables on for degrees and one for tenths of degrees and utilize sin(A + B) = sin(a)cos(b) + cos(A)sin(b)

like image 89
rerun Avatar answered Sep 30 '22 00:09

rerun


For your fastSin(), you should check its documentation to see what range it's valid on. The units you're using for your game could be too big or too small and scaling them to fit within that function's expected range could make it work better.

EDIT:

Someone else mentioned getting it into the desired range by subtracting PI, but apparently there's a function called fmod for doing modulus division on floats/doubles, so this should do it:

#include <iostream>
#include <cmath>

float fastSin( float x ){
    x = fmod(x + M_PI, M_PI * 2) - M_PI; // restrict x so that -M_PI < x < M_PI
    const float B = 4.0f/M_PI;
    const float C = -4.0f/(M_PI*M_PI);

    float y = B * x + C * x * std::abs(x);

    const float P = 0.225f;

    return P * (y * std::abs(y) - y) + y; 
}

int main() {
    std::cout << fastSin(100.0) << '\n' << std::sin(100.0) << std::endl;
}

I have no idea how expensive fmod is though, so I'm going to try a quick benchmark next.

Benchmark Results

I compiled this with -O2 and ran the result with the Unix time program:

int main() {
    float a = 0;
    for(int i = 0; i < REPETITIONS; i++) {
        a += sin(i); // or fastSin(i);
    }
    std::cout << a << std::endl;
}

The result is that sin is about 1.8x slower (if fastSin takes 5 seconds, sin takes 9). The accuracy also seemed to be pretty good.

If you chose to go this route, make sure to compile with optimization on (-O2 in gcc).

like image 44
Brendan Long Avatar answered Sep 30 '22 00:09

Brendan Long