Since trig and inverse trig come up time and time again, I spent a few minutes tossing together a reasonable atan2 approximation. Making good approximations require more work that I’m willing to commit to this function. (Because you really shouldn’t need to call this very much: says the old grump).
private static final float PI = (float)(Math.PI);
private static final float PI_OVER_2 = (float)(Math.PI/2);
private static final float PI_OVER_4 = (float)(Math.PI/4);
/** Return 'x' multiplied by the sign of 'y'. */
// copySign isn't yet an intrinsic, so this isn't a desirable function to call.
public static float mulsign(float x, float y) { return (Math.copySign(1, y) * x); }
/**
* arc tangent of x on [-1,1]
* <p>
* 5th order minimax approximation (minimizing abs error). Banged out quickly,
* a better version could be produced. Error bound of ~0.0006 @
* x = +/-{0.205219, 0.59347, 0.888196, 1.0}
*/
public static final float atan_5(float x)
{
// quality (of either abs or rel error) can be improved at same cost.
// Additionally (by using Estrin's) one could probably produce a higher
// order approximation at same cost (by reducing dependency chains). YMMV.
float x2 = x*x;
return x*(0.995354f+x2*(-0.288769f+0.079331f*x2));
}
/** */
public static final float atan(float y, float x)
{
float ay = Math.abs(y);
float xy = x*y; // this is xor of sign of x & y...probably removable.
float r = 0;
// perform argument reduction. probably can be reduced
if (x < 0) { x = -x; r = -PI; }
if (ay > x) { float t = x; x = ay; ay = -t; r += PI_OVER_2; }
// perform the approximation..increasing speed or accuracy by
// trading the base approximation possible
r += atan_5(ay/x);
// xor the sign of reduced range with the input for final result..
// again all of this could probably be cleaned up with some thought.
return mulsign(r, xy);
}
/**
* atan of x on [0,Inf]
*/
public static final float atanp(float x)
{
// ignoring numeric issues around '1' due to the subtract,
// same error bound as core routine, stretched out over
// the interval.
return PI_OVER_4 + atan_5((x-1)/(x+1));
}
/** atan of x on [-Inf, Inf] */
public static final float atan(float x)
{
float r = atanp(Math.abs(x));
return Math.copySign(r, x);
}