how to calculate sine and cosine in c++ without STL

Started by
22 comments, last by MrRowl 4 years, 6 months ago

Hi, I want to make this forum on how to calculate the sine and cosine in c++ without the STL. I'm sorry if my grammar is not accurate. Well let's get started:

There are several methods to calculate sine and cosine and any other transcendent function.

The Taylor series, polynomial approximations and the CORDIC algorithm "for sine and cosine" are some of these methods.

There are occasions where we have to calculate the same Angle for sine and cosine functions for those cases we can create a single function.

The next function receives 3 parameters the first is the same radian for the cosine and sine and the last two are two variables by reference to store these values. I must clarify that this function has only been tested in video games has not been used for another purpose. This function uses a polynomial approximation.


void sameRadian(const float& rad, float& c, float& s)
{
   union{int i; float f;} u;
	
   u.i = int(rad * 0.31830988f + 0.5f);

   c = (u.i % 2)? 3.14159265f * u.i - rad - 1.570796f : rad + 1.570796f - 3.14159265f * u.i;
	
   u.i = int(rad * 0.31830988f);
	
   s = (u.i % 2)? 3.14159265f * u.i - rad : rad - 3.14159265f * u.i;
	
   //Calculando La Funcion Coseno
   if(c < 0.0f)
   {
      u.f = (1.273239f + c * 0.405284f) * c;
      c = (u.f + 1.0f) * u.f * -0.225f + u.f;
   }
   else
   {
      u.f = (1.273239f - c * 0.405284f) * c;
      c = (u.f - 1.0f) * u.f * 0.225f + u.f;
   }

   //Calculando La Funcion Seno
   if(s < 0.0f)
   {
      u.f = (1.273239f + s * 0.405284f) * s; 
      s = (u.f + 1.0f) * u.f * -0.225f + u.f;
   }
   else
   {
      u.f = (1.273239f - s * 0.405284f) * s; 
      s = (u.f - 1.0f) * u.f * 0.225f + u.f;
   }
}

 

There are several constants and they were placed directly for speed reasons.

 

Here are the two separate functions.

 


float cosf2(const float& rad)
{
   union{int i; float f;} u;

   u.i = int(rad * 0.31830988f + 0.5f);
  
   u.f = (u.i % 2)? 3.14159265f * u.i - rad - 1.570796f : rad + 1.570796f - 3.14159265f * u.i;

   //Calculando La Funcion Coseno
   if(u.f < 0.0f)
   {
      u.f = (u.f * 0.405284f + 1.273239f) * u.f;
      return (u.f + 1.0f) * u.f * -0.225f + u.f;
   }
   else
   {
      u.f = (1.273239f - u.f * 0.405284f) * u.f;
      return (u.f - 1.0f) * u.f * 0.225f + u.f;
   }
}




float sinf2(const float& rad)
{
   union{int i; float f;} u;

   u.i = int(rad * 0.31830988f);
   
   u.f = (u.i % 2)? 3.14159265f * u.i - rad : rad - 3.14159265f * u.i;
	
   //Calculando La Funcion Seno
   if(u.f < 0.0f)
   {
      u.f = (1.273239f + u.f * 0.405284f) * u.f; 
      return (u.f + 1.0f) * u.f * -0.225f + u.f;
   }
   else
   {
      u.f = (1.273239f - u.f * 0.405284f) * u.f; 
      return (u.f - 1.0f) * u.f * 0.225f + u.f;
   }
}

 

well thanks to you for reading my forum.

 

 

 

Advertisement

by the beginning of next year I will launch my educational 2d physics engine CirObb engine with educational purposes to help people understand more about this wonderful world of game physics.

 I am 19 years old and I hope one day to pursue this as a career.

1 hour ago, fleabay said:

Las publicaciones informativas que no responden a una pregunta normalmente se colocan en una publicación de blog y no en el foro. Los foros se denominan comúnmente EL foro y no mi foro o tu foro. Sin embargo, es común llamar a un blog "mi blog".

También una publicación en el blog (y especialmente una publicación en el foro) que comienza con "Cómo ..." será interpretada por muchos mientras buscas ayuda. Simplemente suelte el "Cómo" al comienzo. Aún mejor sería "Calcular seno y coseno en ..."

I really didn't know about the blog, but thanks I had no idea. I am new at this.

Might it be that sin and cos calculation are twisted (no, my mistake :-)) ? Also, I understand it is a demontstrator, but there are some implicit conversions from int to float that might affect the outcome ?

Comparing it to the sincos() from <cmath> (which takes double arguments), the times don't differ by much, but the results do after the second digit behind the decimal, if i haven't made a mistake ...


SameRadian times are better since you avoid calling functions twice. I'm going to place a code fragment that takes the times in microseconds. This code only works on "OS" windows:


#include <Windows.h>
#include <iostream>

#define TIMER_INIT \
LARGE_INTEGER frequency; \
LARGE_INTEGER t1,t2; \
double elapsedTime; \
QueryPerformanceFrequency(&frequency);


#define TIMER_START QueryPerformanceCounter(&t1);

#define TIMER_STOP \
QueryPerformanceCounter(&t2); \
elapsedTime = (float)(t2.QuadPart-t1.QuadPart)/frequency.QuadPart; \
std::cout << elapsedTime * 1000000.0f << " microsecond\n";


void sameRadian(const float& rad, float& c, float& s)
{
	union{int i; float f;} u;
	
	u.i = int(rad * 0.31830988f + 0.5f);

	c = (u.i % 2)? 3.14159265f * u.i - rad - 1.570796f : rad + 1.570796f - 3.14159265f * u.i;
	
	u.i = int(rad * 0.31830988f);
	
	s = (u.i % 2)? 3.14159265f * u.i - rad : rad - 3.14159265f * u.i;
	
	//Calculando La Funcion Coseno
	if(c < 0.0f)
	{
		u.f = (1.273239f + c * 0.405284f) * c;
		c = (u.f + 1.0f) * u.f * -0.225f + u.f;
	}
	else
	{
		u.f = (1.273239f - c * 0.405284f) * c;
		c = (u.f - 1.0f) * u.f * 0.225f + u.f;
	}

	//Calculando La Funcion Seno
	if(s < 0.0f)
	{
		u.f = (1.273239f + s * 0.405284f) * s; 
		s = (u.f + 1.0f) * u.f * -0.225f + u.f;
	}
	else
	{
		u.f = (1.273239f - s * 0.405284f) * s; 
		s = (u.f - 1.0f) * u.f * 0.225f + u.f;
	}
}



int main(int args, char** argv)
{
	float cose, seno, DEG2RAD = 3.14159265358979f / 180;
	
	while(true)
	{
		TIMER_INIT 
		TIMER_START

		for(int i = 0; i <= 1000; i++) sameRadian(i * DEG2RAD, cose, seno);

		/*
		for(int i = 0; i <= 1000; i++)
		{ 
		   sinf(i * DEG2RAD); 
		   cosf(i * DEG2RAD);
		}
        */
		TIMER_STOP
	}
}

 

try it and tell me what results it shows you

Why bother with the union for i and f?

Stephen M. Webb
Professional Free Software Developer

Hola,

on Linux, the results are off. For example, sin(PI/4): sameRadians has 0.707816 compared to 0.707107 by sincos(). For a moon shot, that'll be a miss or an impact ?

2 hours ago, Green_Baron said:

Hola,

For example, sin(PI/4): sameRadians has 0.707816 compared to 0.707107 by sincos(). 

PI / 4 is the case of least convergence and the result is: 0.707816 three digits of precision. As I said from the beginning it is good only for video games. I have used this function a huge number of times to draw circles and calculate rotation matrices without any problem.

Well in this case, it turns out to be ~50% faster than the built in, a gain of .08 seconds for 10 million calls in a loop (debug build). If there are 10s or hundreds of millions of operations and accuracy is no concern, then why not.

Tradeoff between accuracy and speed can have extreme cases:

Spoiler


double FastSin(double x)
{
    return 0.0;
}

(from a stackoverflow post).

This is only half meant as a joke, the fastest optimization is not to use a function or to use it when time doesn't matter that much ... my concerns were also the remarks of others here hinting to undefined behaviour.

This topic is closed to new replies.

Advertisement