Welcome to Dream.In.Code
Getting C++ Help is Easy!

Join 119,725 C++ Programmers for FREE! Ask your question and get quick answers from experts. There are 1,479 online right now! We've got more than 500 tutorials and 2,000 snippets. Join and find out why Dream.In.Code is the #1 programming help community on the internet! Registration is fast and FREE... Join Now!



sin() and cos() equivalent

2 Pages V  1 2 >  
Reply to this topicStart new topic

sin() and cos() equivalent

prads
post 20 Jun, 2008 - 01:13 PM
Post #1


D.I.C Head

**
Joined: 22 Oct, 2007
Posts: 108


My Contributions


Hello,
I am using sin() and cos() built in functions in my C++ code. However after profiling it I realized that the 2 functions consume a lot of time. So I decided to eliminate sin() and cos() functions and replace it with an equivalent code that generates the same values for me, using either trigonometric identities or something else (Please suggest!). I tried an approach, which is shown below, but I am not getting the required results due to errors in it (The problem I assume is because of the large number of loops it goes through -- 35000*35000 which marginally moves it away from the desired result). Can somebody help me with this or another approach which might be more precise and correct. I have not shown the entire code because its big and unwanted. I just want a replacement for sin() and cos().

CODE


// This is the code that works correctly but consumes a lot of time
main()
float phase=0.0, rate=2.5e6, raw[35000],multcos[35000],multsin[35000];
{
//some statements here

for(int i=0;i<35000;i++)
{
//some statements here

for(int j=0;j<35000;j++)
{
arg=(2*3.14*fc*j)/rate  + phase;
multcos[j]=raw[j]*cos(arg); //I want to replace this cos()
multsin[j]=raw[j]*sin(arg);  //I want to replace this sin()
//raw[j] has some predefined values

Sum1[H[j]]=Sum1[H[j]] + multcos[j];
Sum2[H[j]]=Sum2[H[j]] + multsin[j];
//H[j] has some predefined values;
//Sum1[] has been initilized to 0;

}
//some statements here
}

//some statements here
}//end of main



Below is the replacement/equivalent code I wrote to replace sin and cos functions.


CODE

//This is the equivalent code that does not work correctly

float G_cb, G_b=1.57071406,    G_a=0.0;
    float G_snm2,    G_snm1,    G_cnm2, G_cnm1;
    float G_s,G_c;

for(i=0;i<35000;i++)
{
    G_cb = 2* cos(G_b * PI/180);
    G_snm2 = sin(G_a + G_b);  G_snm1 = sin(G_a + 2*G_b);
    G_cnm2 = cos(G_a + G_b);  G_cnm1 = cos(G_a + 2*G_b);

    for(int j=0;j<35000;j++)
    {
    G_s = (G_cb * G_snm1) - G_snm2;
    G_c = (G_cb * G_cnm1) - G_cnm2;
    G_snm2 = G_snm1;  G_cnm2 = G_cnm1;
    G_snm1 = G_s;  G_cnm1 = G_c;

        multcos[j]= raw[j] * G_c;
    multsin[j]= raw[j] * G_s;
        
       Sum1[H[j]]=Sum1[H[j]] + multcos[j];
       Sum2[H[j]]=Sum2[H[j]] + multsin[j];
       //H[j] has some predefined values;
       //Sum1[] has been initilized to 0;

     }
}


Please help me.
Thanks,
prads
User is offlineProfile CardPM

Go to the top of the page


Tom9729
post 20 Jun, 2008 - 02:08 PM
Post #2


Debian guru

Group Icon
Joined: 30 Dec, 2007
Posts: 1,429



Thanked 10 times

Dream Kudos: 325
My Contributions


Try one of the links here.

I don't know if writing your own trig functions is a good idea unless you really know what you're doing. smile.gif
User is offlineProfile CardPM

Go to the top of the page

perfectly.insane
post 20 Jun, 2008 - 04:07 PM
Post #3


D.I.C Addict

Group Icon
Joined: 22 Mar, 2008
Posts: 550



Thanked 44 times

Dream Kudos: 25

Expert In: C/C++

My Contributions


Perhaps a lookup table would help your application?

cpp


struct value_item
{
bool is_valid;
double result;
};

const int quarter_table_size = (int)(3.14159 / 0.0002);

value_item sin_table[quarter_table_size];
value_item cos_table[quarter_table_size];

void init_tables()
{
std::memset(sin_table, 0, sizeof(sin_table));
}

double dyn_sin(double v)
{
int k = (int)(v * 10000) % (quarter_table_size * 4);
double mul = 1.0;

if(k >= quarter_table_size && k < quarter_table_size * 2) k = (quarter_table_size * 2) - k;
else if(k >= quarter_table_size * 2 && k < quarter_table_size * 3) { k = k - (quarter_table_size * 2); mul = -1.0; }
else if(k >= quarter_table_size * 3) { k = (quarter_table_size * 4) - k; mul = -1.0; }

if(!sin_table[k].is_valid)
{ sin_table[k].value = sin((double)k / 10000.0);
sin_table[k].is_valid = true;
}

return sin_table[k].value * mul;
}



The less precision that you need, the faster this will be. You could even precompute the table and have it in a separate file, ready to use (no runtime overhead).

This post has been edited by perfectly.insane: 20 Jun, 2008 - 04:16 PM
User is offlineProfile CardPM

Go to the top of the page

prads
post 23 Jun, 2008 - 03:56 PM
Post #4


D.I.C Head

**
Joined: 22 Oct, 2007
Posts: 108


My Contributions


Hello,
Thank you for the code. I havent tried it yet but will try soon. I have one question: For cos(v), should I just do v=v+PI/2 within the function "double dyn_sin(double v) " or do I have to wrap it up with the following:

CODE

double dyn_sin(double v)  
{  
v=v+PI/2;
if(v>PI)
v=v-2*PI;
int k = (int)(v * 10000) % (quarter_table_size * 4);  
  double mul = 1.0;  
// etc
//etc
}


Thanks a lot,
prads
User is offlineProfile CardPM

Go to the top of the page

perfectly.insane
post 23 Jun, 2008 - 06:55 PM
Post #5


D.I.C Addict

Group Icon
Joined: 22 Mar, 2008
Posts: 550



Thanked 44 times

Dream Kudos: 25

Expert In: C/C++

My Contributions


I'd think that you'd be able to define it directly in terms of dyn_sin, as you stated.

cpp

double dyn_cos(double v) { return dyn_sin(v + (PI / 2.0)); }


Again, if a dynamic solution isn't efficient enough, the table can be statically generated, in which the time required will always be the same. This would be especially useful if v is different for each call to dyn_sin.
User is offlineProfile CardPM

Go to the top of the page

prads
post 25 Jun, 2008 - 05:49 PM
Post #6


D.I.C Head

**
Joined: 22 Oct, 2007
Posts: 108


My Contributions


Hello perfectly.insane,
I am sorry but I couldn't understand few things in your code. First of all, to make it run i made the following changes(shown below as code). My doubts are:
Do I have to run the code separately and then store all the values in the arrays sin_table[quarter_table_size] and cos_table[quarter_table_size], and then pull the values for its respective arguments? i.e do I have to manually generate sin and cos values in radians and store it first before running your code?
What changes to be made if I want to generate it statically (because the value of v is different each time)?
I ran the below code but it has no value stored in the array sin_table[quarter_table_size]. Please make it more clear for me.
Also i didnot understand the working of void init_tables() . Is this storing the sin and cos values?

CODE

#include <cstdlib>
#include <iostream>
#include <cmath>
#define quarter_table_size  (int)(3.14159 / 0.0002)

struct value_item
{
    bool is_valid;
    double result;
}sin_table[quarter_table_size],cos_table[quarter_table_size];

using namespace std;

int main()
{

double dyn_sin(double v);
void init_tables();
double sin_val;

sin_val = dyn_sin(15.7); //for example
system("PAUSE");
return 0;
}


double dyn_sin(double v)
{
     int k = (int)(v * 10000) % (quarter_table_size * 4);
     double mul = 1.0;
    
     if(k >= quarter_table_size && k < quarter_table_size * 2) k = (quarter_table_size * 2) - k;
     else if(k >= quarter_table_size * 2 && k < quarter_table_size * 3) { k = k - (quarter_table_size * 2); mul = -1.0; }
     else if(k >= quarter_table_size * 3) { k = (quarter_table_size * 4) - k; mul = -1.0; }
    
     if(!sin_table[k].is_valid)
     {   sin_table[k].result = sin((double)k / 10000.0);
          sin_table[k].is_valid = true;
     }
    
     return sin_table[k].result * mul;
}


void init_tables()
{
     std::memset(sin_table, 0, sizeof(sin_table));
}




Thanks a lot,
prads

This post has been edited by prads: 25 Jun, 2008 - 05:52 PM
User is offlineProfile CardPM

Go to the top of the page

perfectly.insane
post 26 Jun, 2008 - 07:47 PM
Post #7


D.I.C Addict

Group Icon
Joined: 22 Mar, 2008
Posts: 550



Thanked 44 times

Dream Kudos: 25

Expert In: C/C++

My Contributions


QUOTE(prads @ 25 Jun, 2008 - 05:49 PM) *

Do I have to run the code separately and then store all the values in the arrays sin_table[quarter_table_size] and cos_table[quarter_table_size], and then pull the values for its respective arguments? i.e do I have to manually generate sin and cos values in radians and store it first before running your code?


No, you don't. Looking at the following section:

cpp

if(!sin_table[k].is_valid)
{ sin_table[k].result = sin((double)k / 10000.0);
sin_table[k].is_valid = true;
}


Since the sin_table is zero'ed in init_tables, the is_valid member of each structure is false by default, and is only true once the inside of that if block is executed (which also uses the library sin function to calculate the value). So it fills the entries on demand.

You don't need a separate cosine table, as cosine is the same as sine shifted 90 degrees (as shown by my definition of dyn_cos in my previous post).

QUOTE
What changes to be made if I want to generate it statically (because the value of v is different each time)?


One way that you could do this would be by taking the core of dyn_sin so that it computes all values at once, such as:

cpp

void compute_sin_table()
{
for(k = 0; k < quarter_table_size; k++)
sin_table[k].result = sin((double)k / 10000.0);
}

double static_sin(double v)
{
int k = (int)(v * 10000) % (quarter_table_size * 4);
double mul = 1.0;

if(k >= quarter_table_size && k < quarter_table_size * 2) k = (quarter_table_size * 2) - k;
else if(k >= quarter_table_size * 2 && k < quarter_table_size * 3) { k = k - (quarter_table_size * 2); mul = -1.0; }
else if(k >= quarter_table_size * 3) { k = (quarter_table_size * 4) - k; mul = -1.0; }

return sin_table[k].result * mul;
}

double static_cos(double v)
{
return static_sin(v + (PI / 2.0));
}



Of course, each element of sin_table could now be a double instead of a struct, because is_valid is no longer needed.

For optimal effect, you could write a program that computes this table, and prints it out in the form of a C++ snippet. It would need to print something like:

CODE

double sin_table[quarter_table_size] = {
    0.00000, 0.00001, ......
};


So that you can copy and paste that hard-coded array into your program, so the table never needs to be computed to begin with.


QUOTE
I ran the below code but it has no value stored in the array sin_table[quarter_table_size]. Please make it more clear for me.


The code will compute one value at a time, and will cache the value, so the next time the function is called with the same value, it just returns the stored value in sin_table.

QUOTE
Also i didnot understand the working of void init_tables() . Is this storing the sin and cos values?

No. This initializes the sin_table array to all zeros, so that all is_valid items are false initially. The dyn_sin function caches the values on demand.

If you call dyn_sin(0.3) for the first time, the library sin function is called. If you call it again (with the same value, 0.3), it takes it from the lookup table, as the function records the result from sin whenever it calls it.

User is offlineProfile CardPM

Go to the top of the page

born2c0de
post 26 Jun, 2008 - 10:54 PM
Post #8


printf("I'm a %XR",195936478);

Group Icon
Joined: 26 Nov, 2004
Posts: 3,815



Thanked 27 times

Dream Kudos: 2800

Expert In: 80x86 Assembly, C/C++, VB6, VB.NET, C#, J2SE, Win32 API, Reversing

My Contributions


You can also generate sin() and cos() using its standard series
IPB Image
IPB Image

or from its Maclaurin Series:
IPB Image
IPB Image
User is offlineProfile CardPM

Go to the top of the page

NickDMax
post 27 Jun, 2008 - 07:59 AM
Post #9


2B||!2B

Group Icon
Joined: 18 Feb, 2007
Posts: 2,774



Thanked 38 times

Dream Kudos: 525
My Contributions


Although using a Taylor or Maclaurin series seems like a good idea it would probably be slower than using the built in functions (you could use assembly language optimization... but I still think you would be slower).

The usual solution to this is a look-up table. However if you need higher precision and accuracy you can use a look-up table in conjunction with one of the approximation algorithms (interpolation).

Generally in the past when I have done this I used a lookup table and linear interpolation but with lookup point at about every 1 degree but a 3 or 4 point polynomial interpolation should still be pretty fast and have a high degree of accuracy.
User is offlineProfile CardPM

Go to the top of the page

born2c0de
post 28 Jun, 2008 - 05:06 AM
Post #10


printf("I'm a %XR",195936478);

Group Icon
Joined: 26 Nov, 2004
Posts: 3,815



Thanked 27 times

Dream Kudos: 2800

Expert In: 80x86 Assembly, C/C++, VB6, VB.NET, C#, J2SE, Win32 API, Reversing

My Contributions


QUOTE
Although using a Taylor or Maclaurin series seems like a good idea it would probably be slower than using the built in functions (you could use assembly language optimization... but I still think you would be slower).

Agreed, but what assembly optimizations are you referring to?
Wouldn't it be easier (and of course faster) to use the FSIN, FCOS and FSINCOS instructions for x86 (and above) computers?
User is offlineProfile CardPM

Go to the top of the page

NickDMax
post 28 Jun, 2008 - 10:24 AM
Post #11


2B||!2B

Group Icon
Joined: 18 Feb, 2007
Posts: 2,774



Thanked 38 times

Dream Kudos: 525
My Contributions


Probably.
User is offlineProfile CardPM

Go to the top of the page

perfectly.insane
post 28 Jun, 2008 - 10:54 PM
Post #12


D.I.C Addict

Group Icon
Joined: 22 Mar, 2008
Posts: 550



Thanked 44 times

Dream Kudos: 25

Expert In: C/C++

My Contributions


fsin appears to be much faster than the library functions. A lookup table can still be faster though, but the precision is proportional to the lookup table size. The performance difference isn't very much though (fsin is about 1.6 times slower on my test program, and the library function is 7.2 times slower).
User is offlineProfile CardPM

Go to the top of the page

2 Pages V  1 2 >
Reply to this topicStart new topic
Time is now: 10/15/08 03:52PM

Live C++ Help!

C++ Tutorials

Reference Sheets

C++ Snippets

Bye Bye Ads

Free DIC T-Shirt

T-Shirt Example

Related Sites

Monthly Drawing

Thumb Drive

Partners

Top Contributors

Top 10 Kudos This Month