Hi folks,
I’ve created an algorithm that allows atan2 to be calculated through a lookup-table.
All values for Y and X are allowed. You can control the RAM-usage/accuracy by setting the bits used in both dimensions.
I used 7 bits per dimension - that means 2^7 (128) values for each dimension: so 128*128 = 16k entries in lookup-table
this results in an accuracy of 0,0078125 (1.0 / 128) in each dimension after the coords are normalized
Updated
private static final int ATAN2_BITS = 7;
private static final int ATAN2_BITS2 = ATAN2_BITS << 1;
private static final int ATAN2_MASK = ~(-1 << ATAN2_BITS2);
private static final int ATAN2_COUNT = ATAN2_MASK + 1;
private static final int ATAN2_DIM = (int) Math.sqrt(ATAN2_COUNT);
private static final float INV_ATAN2_DIM_MINUS_1 = 1.0f / (ATAN2_DIM - 1);
private static final float DEG = 180.0f / (float) Math.PI;
private static final float[] atan2 = new float[ATAN2_COUNT];
static
{
for (int i = 0; i < ATAN2_DIM; i++)
{
for (int j = 0; j < ATAN2_DIM; j++)
{
float x0 = (float) i / ATAN2_DIM;
float y0 = (float) j / ATAN2_DIM;
atan2[j * ATAN2_DIM + i] = (float) Math.atan2(y0, x0);
}
}
}
/**
* ATAN2
*/
public static final float atan2Deg(float y, float x)
{
return FastMath.atan2(y, x) * DEG;
}
public static final float atan2DegStrict(float y, float x)
{
return (float) Math.atan2(y, x) * DEG;
}
public static final float atan2(float y, float x)
{
float add, mul;
if (x < 0.0f)
{
if (y < 0.0f)
{
x = -x;
y = -y;
mul = 1.0f;
}
else
{
x = -x;
mul = -1.0f;
}
add = -3.141592653f;
}
else
{
if (y < 0.0f)
{
y = -y;
mul = -1.0f;
}
else
{
mul = 1.0f;
}
add = 0.0f;
}
float invDiv = 1.0f / (((x < y) ? y : x) * INV_ATAN2_DIM_MINUS_1);
int xi = (int) (x * invDiv);
int yi = (int) (y * invDiv);
return (atan2[yi * ATAN2_DIM + xi] + add) * mul;
}
The lengthy if/else-chain might seem inefficient, but it is really the fastest way to normalize X and Y.
Benchmark:
float min = -100;
float max = +100;
float step = 0.12f;
for (int i = 0; i < 8; i++)
{
long t0A = System.nanoTime() / 1000000L;
float sumA = 0.0f;
for (float y = min; y < max; y += step)
for (float x = min; x < max; x += step)
sumA += FastMath.atan2(y, x);
long t1A = System.nanoTime() / 1000000L;
long t0B = System.nanoTime() / 1000000L;
float sumB = 0.0f;
for (float y = min; y < max; y += step)
for (float x = min; x < max; x += step)
sumB += Math.atan2(y, x);
long t1B = System.nanoTime() / 1000000L;
System.out.println();
System.out.println("FastMath: " + (t1A - t0A) + "ms, sum=" + sumA);
System.out.println("JavaMath: " + (t1B - t0B) + "ms, sum=" + sumB);
System.out.println("factor: " + ((float) (t1B - t0B) / (t1A - t0A)));
}
Results:
...
FastMath: 84ms, sum=-2705.0369
JavaMath: 1161ms, sum=-2690.9863
factor: 13.821428
FastMath: 84ms, sum=-2705.0369
JavaMath: 1164ms, sum=-2690.9863
factor: 13.857142
Have fun! (or beat me!)