3x faster acos() function

Why doesn't the online reference manual show all the available trig functions?

I found out native acos() exists, but only after implementing my own acos.

Turns out, a hand coded acos function runs 3x faster than the one implemented in the Arduino base language...

float myacos(float x)
// this routine is about 3x the speed of built-in acos
{
float negate = float(x < 0);
x = abs(x);
float ret = -0.0187293;
ret = ret * x;
ret = ret + 0.0742610;
ret = ret * x;
ret = ret - 0.2121144;
ret = ret * x;
ret = ret + 1.5707288;
ret = ret * sqrt(1.0 - x);
ret = ret - 2 * negate * ret;
return negate * 3.14159265358979 + ret;
}

and has very close results (close enough for my purposes).. maybe for you too! if you're doing a lot of trig and all speed matters

1 Like

Why doesn't the online reference manual show all the available trig functions?

Because he Arduino reference pages don't attempt to teach you C/C++

UKHeliBob:
Because he Arduino reference pages don't attempt to teach you C/C++

..not making sense.

Yes, arduino Arduino - Home
attempts and does show some Trigonometry functions, sin() cos() and tan().

So, why not list them all... ?

OP: we appreciate the alternative code. It would help us if, for any other contributions, you would take a look at Nick Gammon's post at the top of this Forum on how to post source code using code tags. Also, before posting and with your code in the IDE, use Ctrl-T to reformat it in a style that most of us find easier to read. Also, you can write the function using the "?=" operators:

float otheracos(float x)
{
  float negate = float(x < 0);
  float ret = -0.0187293;
  x = abs(x);
  ret = x * -0.0187293;
  ret += 0.0742610;
  ret *= x;
  ret -= 0.2121144;
  ret *= x;
  ret += 1.5707288;
  ret *= sqrt(1.0 - x);
  ret = ret - 2.0 * negate * ret;
  return negate * 3.14159265358979 + ret;
}

The compiler is smart enough that this probably makes no different to the speed or code size, but are common idioms many will use. As far as readability, your version is probably easier to read.

So, why not list them all... ?

Why does it not list all of the C and C++ functions ?

Because it is not a C/C++ reference, of which there are many on line.

You will also discover the PI is defined in the math library.

Got it! I was relying on Arduino reference page too much, it's clearly not meant to be a complete reference.

But in the end... made a spanking improvement in acos() speed!

Also, I was looking for a button in the post dialog to "insert code sample" (that would be handy!) but can live with manually tagging code.

// like this comment

Color me learned

scottmcphee:
Color me learned

You could reciprocate and document the program. It's uncommented and full of Magic Numbers. There must be a source for the technique. You should at least include a link to the sources in a comment line.
"1.5707288" looks suspiciously like PI/2. The compiler will perform optimization on constant expressions. You don't need to worry about the divide.

made a spanking improvement in acos() speed!

Accuracy suffers, though.

float otheracos(float x)
{
  float negate = float(x < 0);
  float ret = -0.0187293;
  x = abs(x);
  ret = x * -0.0187293;
  ret += 0.0742610;
  ret *= x;
  ret -= 0.2121144;
  ret *= x;
  ret += 1.5707288;
  ret *= sqrt(1.0 - x);
  ret = ret - 2.0 * negate * ret;
  return negate * 3.14159265358979 + ret;
}

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  float x = 0.;
  for (x = 0; x < 1.; x += .01) {
    Serial.print(acos(x), 6);
    Serial.print("\t");
    Serial.println(otheracos(x), 6);
  }
}

void loop() {
  // put your main code here, to run repeatedly:

}
1.570796	1.570729
1.560796	1.560752
1.550795	1.550772
1.540792	1.540786
1.530786	1.530796
1.520775	1.520799
1.510760	1.510794
1.500739	1.500782
1.490711	1.490761
...

It's not "my" acos()... as in my invention... it's code copied right from here c++ - Fast Arc Cos algorithm? - Stack Overflow

giving credit where due. I think the source of this is with nVidia, the graphics people.

I only used "myacos()" and "my own code" to differentiate from what the Arduino system supplies as its built-in acos()

As the chances of anybody knowing about or remembering the Nvidia code are extremely small, I think it would be an obvious necessity to make that explicit if the word "my" is used. Especially in a forum where people come for help with programming projects.

Yes, accuracy suffers.

Very close results (close enough for my purposes).. I only need to know if angle of two vectors are within 10 degrees or so of each other. This is more than enough accuracy

if you're doing a lot of trig and all speed matters, over accuracy, then 3 for the price of 1 could be a good deal!

scottmcphee:
I only need to know if angle of two vectors are within 10 degrees or so of each other. This is more than enough accuracy

Then a look up table will beat this implementation hands down.

aarg:
Then a look up table will beat this implementation hands down.

..as would be the case for any function, if memory were not a premium

scottmcphee:
..as would be the case for any function, if memory were not a premium

I was not making a general statement. You require very low resolution. If you can't spare 144 bytes of RAM, you could put the table in PROGMEM.

While were here, how about a function header convention for new stuff like that being discussed here. As an example:

/*****
   Purpose: To return the inverse for the cosine value supplied. Algorithm taken from:
                  https://stackoverflow.com/questions/3380628/fast-arc-cos-algorithm

   Parameter list:
      float x            a cosine with the range -1 to + 1

   Return value:
      float              the inverse of the parameter

   CAUTION: current function does not check parameter range and may return NAN
*****/
float NewAcos(float x) 
{
    // and the rest of the code

I've used this format on many complex projects because I have a filter program that can read in the source code files, look for "/*****" and once found, copy through to the closing parenthesis at the end of the function signature, use FILE and LINE and save the results to a file. (I also sorted on the function name.) The end result was a file that documented all of the functions written for the project.

Also, it would be handy if those posting code that use non-IDE libraries to give the rest of us a link to the library. I use something like this:

#include <AD9850SPI.h>        // https://github.com/F4GOJ/AD9850SPI
#include <NewTone.h>          // https://forum.arduino.cc/index.php?topic=143940.0
#include <EEPROM.h>           // Standard with IDE
#include <SD.h>               // Standard with IDE

That would make it easier on those of us who would like to try the code.

econjack:
Also, it would be handy if those posting code that use non-IDE libraries to give the rest of us a link to the library. I use something like this:

#include <AD9850SPI.h>        // https://github.com/F4GOJ/AD9850SPI

I actually do that with all my downloaded library includes. Not just for other people, for me.

aarg:
I actually do that with all my downloaded library includes. Not just for other people, for me.

I agree. Can't tell you how many times I've used that info to check something out.

scottmcphee:
Very close results (close enough for my purposes).. I only need to know if angle of two vectors are within 10 degrees or so of each other. This is more than enough accuracy

If you only care about the angle between two vectors, it may be even quicker to normalize the vectors and then compute the dot product. This will give you the cosine of the angle between the vectors.