For now, all you need to know is that it takes the coordinates of a 2D point (two numbers), and returns an angle between −π and +π.

Nowadays atan2 finds its place in all programming languages, including in C thorugh libc. For floats, this is what it looks like:

float atan2f(float y, float x);

Sadly atan2f is not the fastest function out there. In fact, if you write software relying on it, your profiler might tell you that you’re spending a significant amout of time in atan2f.1

To measure how slow it is, let’s set up a simple function, atan2_baseline, where we compute atan2f on a bunch of points, stored in row-major order:

void atan2_baseline(size_t num_points, const float* ys, const float* xs, float* out) {
  for (size_t i = 0; i < num_points; i++) {
    out[i] = atan2f(ys[i], xs[i]);
  }
}

Chart showing the performance of our \mathrm{atan2} atan2 approximations.

Plot showing the results of \mathrm{atan2} atan2 in degrees for points in each quadrant. Note the negative sign for points below the x x axis.

Plot showing the relationship between \sin sin, \cos cos, \tan tan, and \arctan arctan for some angle \theta θ. Note that while \cos \theta cosθ comes first in the coordinates, it is the denominator in \tan tan. This explains why y y comes before x x in the arguments of \mathrm{atan2} atan2.

Plot showing how to adjust the results of \arctan arctan. Remember that the angles below the x x line are negative, so for example in the second quadrant \pi + \arctan(y/x)π+ arctan(y/x) will be positive, since \arctan(y/x) arctan(y/x) will be in [-\pi/2, 0] [−π/2,0].

To cover the end points, we can tweak the coefficients to minimize the max error within our interval. This will basically involve “stretching” the approximation a bit so that it doesn’t return any egregiously bad results.

We won’t cover the details of how the efficients are tuned,7 but if you browse the libc sources provided by your system you will find magic numbers which are none other than optimized coefficients to power series.