Sine Studies


There are a lot of cases where you need the values of the sine function. And there are a lot of ways to do it. Obviously, they all differ in speed, space and precision.

With the sketch listed below you can compare the reqirements of the algorithms and pick the one which suits you best.

The algorithms used and their results are:
method speed. time [microseconds] space [bytes] max error
precalculated and stored in RAM    136 256 1 bit
precalculated and stored in ROM    180 256 1 bit
calculated at runtime with the sine function 40164 0 0
calculated with the power series, only 2 terms (precision insufficient) 55856 0 0.0737
calculated with the power series, 3 terms sufficient) 70228 0 0.0044
calculated with the power series, 4 terms (somewhat better) 84284 0 0.0002
calculated with the power series, 4 terms (trying to speed up with x2=x*x) 80288 0 0.0002
calculated with the power series, 4 terms (HORNER) 80796 0 0.0002
calculated with the power series, 4 terms (HORNER reversed order) 80216 0 0.0002
calculated by rotating a vector in the cartesian system 54196 0 0.0061

All the results shown above were obtained by using gcc.exe (WinAVR 20081205) 4.3.2.
The references for the error values are the values obtaind by the built-in sine function.
As you can see all efforts of overtaking the compiler are interesting but useless.
The last one might be useful in special cases as it gives you sine and cosine in one go.

#define FILENAME "SineStudies"

#define DEST PORTB
// surprisingly, these defines really work:
#define FOR for(i=0;i<256;i++)           // the loop 
#define I2X x=PI*i/512;                  // integer to floating point
#define F2B 128+127*                     // convert float to byte
#define ERROR df=max(df,abs(y-sin(x)));  // return the max error
// you can remove the "//" in the following line to watch the calculated values
#define PRINT //Serial.println(DEST);
#include <avr/pgmspace.h>

const byte sinRam[] = {
#include "sintab.h"
};

const byte sinFlash[] PROGMEM = {
#include "sintab.h" // the same table, of course
};

const byte RAM = 0;
const byte FLASH = 1;
const byte SINE  = 2;
const byte SERIES2 = 3;
const byte SERIES3 = 4;
const byte SERIES4 = 5;
const byte SERIES4a = 6;
const byte SERIES4b = 7;
const byte SERIES4c = 8;
const byte ROTATION = 9;
const float PHI = -PI/512;
float SIN = sin(PHI);
float COS = cos(PHI);

const String name[] = { 
   "RAM  ", 
   "FLASH", 
   "calculating the sine function    ", 
   "calculating the power series (2) ", 
   "calculating the power series (3) ", 
   "calculating the power series (4) ", 
   "calculating the power series (4a)", 
   "calculating the power series (4b)", 
   "calculating the power series (4c)", 
   "calculating by rotating" 
 };
   
void setup() {
  Serial.begin(9600);
  Serial.println(F(FILENAME));
  for (byte method = RAM; method <= ROTATION; method++) 
    performTest(method);
}

void loop() {}

void performTest(byte method) {  
  Serial.println();
  Serial.print("method: ");
  Serial.print(name[method]);
  Serial.println(F(": "));
  float x;
  float y = 0;
  float z = 1;
  float df = 0;
  int i;
  long t1 = micros();
  switch (method) {
    case RAM:      FOR {DEST = sinRam[i];PRINT} break;
    case FLASH:    FOR {DEST = pgm_read_byte_near(sinFlash + i);PRINT} break;
    case SINE:     FOR {I2X y=sin(x);DEST=F2B y;ERROR PRINT} break;
    case SERIES2:  FOR {I2X y=(x-x*x*x/6);DEST=F2B y;ERROR PRINT} break;
    case SERIES3:  FOR {I2X y=(x-x*x*x/6+x*x*x*x*x/120);DEST=F2B y;ERROR PRINT} break;
    case SERIES4:  FOR {I2X y=(x-x*x*x/6+x*x*x*x*x/120-x*x*x*x*x*x*x/5040);DEST=F2B y;ERROR PRINT} break;
    case SERIES4a: FOR {I2X float x2=x*x;y=(x-x*x2/6+x*x2*x2/120-x*x2*x2*x2/5040);DEST=F2B y;ERROR PRINT} break;
    case SERIES4b: FOR {I2X float x2=-x*x;y=x*(1+x2/6*(1+x2/20*(1+x2/42)));DEST=F2B y;ERROR PRINT} break;
    case SERIES4c: FOR {I2X float x2=-x*x;y=(((1+x2/42)*x2/20+1)*x2/6+1)*x;DEST=F2B y;ERROR PRINT} break;
    case ROTATION: FOR {I2X float x1=-z;z=z*COS+y*SIN;y=x1*SIN+y*COS;DEST=F2B y;ERROR PRINT} break;
  } 
  long t2 = micros();
  Serial.print(F("Time : "));
  Serial.print(t2 - t1);
  Serial.println(F(" microseconds"));
  if (method < SINE) return;
  Serial.print(F("Error: "));
  Serial.println(df,4);
}



contact: nji(at)gmx.de