Skip to main content

Arctangent Lookup Table (atantab)

Inverse trigonometric function module based on a precomputed lookup table, providing both integer and floating-point precision. It is the inverse counterpart of the forward Cosine Lookup Table (costab), sharing the same 16-bit fixed-point angle convention, so results can be fed straight back into isin()/icos().

Principle

The forward table (costab) is indexed by a uniformly sampled angle and is naturally well-conditioned. Inverse trigonometric functions instead take a ratio as input, where asin/acos have an unbounded derivative as |x| -> 1; uniformly sampling the input value would incur huge errors near the endpoints.

To stay well-conditioned everywhere, the module only tabulates atan(t) (t in [0, 1], whose derivative 1/(1+t^2) is bounded in [0.5, 1]) and derives the rest via identities:

  • asin(x) = atan2(x, sqrt(1 - x^2))
  • acos(x) = atan2(sqrt(1 - x^2), x)

This pushes the singularity entirely onto the square root, keeping the table-lookup part benign everywhere.

atantab[i] = atan(i / 1024), with 1025 entries (the last one being the atan(1) = pi/4 endpoint, so linear interpolation at the top needs no clamping). Linear interpolation between adjacent samples yields accuracy far finer than the sample spacing.

atan2 is implemented via first-quadrant reduction plus quadrant restoration: |y| and |x| are compared to squeeze the ratio into [0, 1], the table yields an angle in [0, pi/2], and the signs of x and y then map it into the correct quadrant.

Lookup Tables

extern const int16_t iatantab[1025]; /* Integer atan table, angle units [0, 8192] = [0, pi/4] */
extern const float fatantab[1025]; /* Floating-point atan table, radians [0, pi/4] */

API

Integer Inverse Trigonometric Functions

int iatan2(int y, int x); /* Returns a 16-bit fixed-point angle, [-32768, 32767] = [-pi, pi) */
int iatan(int t); /* t is a 1.10 fixed-point ratio (1024 == 1.0), returns [-8192, 8192] = [-pi/4, pi/4] */
int iasin(int v); /* v is a unit-amplitude value (32767 == 1.0), returns a 16-bit fixed-point angle */
int iacos(int v); /* v is a unit-amplitude value (32767 == 1.0), returns a 16-bit fixed-point angle */

Angles use 16-bit fixed-point units (2*pi == 65536), identical to costab, so the result of iatan2() can be passed directly to isin()/icos(). iasin(v)/iacos(v) take a unit-amplitude value in [-32767, 32767] (32767 corresponds to 1.0, matching icostab); the integer square root reuses sqrti(). iatan2(0, 0) is defined to return 0.

iatan2() accepts arbitrary integer inputs; magnitudes up to roughly 2*10^6 are safe with 32-bit intermediate arithmetic.

Floating-Point Inverse Trigonometric Functions

float fatan2(float y, float x); /* Returns radians, [-pi, pi] */
float fatan(float t); /* Clamped to |t| <= 1, returns [-pi/4, pi/4] */
float fasin(float x); /* Clamped to |x| <= 1, returns [-pi/2, pi/2] */
float facos(float x); /* Clamped to |x| <= 1, returns [0, pi] */

Inputs are standard floating-point values, angles are returned in radians. fasin()/facos() use sqrtf() internally.

Angle Reference (same as costab)

Angle valueRadiansDegrees
0x00000
0x4000pi/290°
0x8000pi180°
0xC0003pi/2270°
0xFFFF~2pi~360°

Radians to 16-bit fixed-point angle: x = (int)(radians / (2 * PI) * 65536)

Accuracy

With 1024 samples plus linear interpolation, the measured maximum error (vs. the standard library) is:

FunctionMax error
fatan2 / fatan / fasin / facos~ 5x10^-7 rad (~ 0.00003°)
iatan2 / iasin / iacos~ 1.6x10^-4 rad (~ 0.009°)

The floating-point version reaches the float precision limit; the integer version is bottlenecked by the 16-bit angle output quantization (1~2 LSB), the table itself no longer being the limiting factor.

Usage Examples

Direction angle from coordinates (integer)

int dx = target_x - origin_x;
int dy = target_y - origin_y;
int angle = iatan2(dy, dx); /* 16-bit fixed-point angle, passable directly to isin()/icos() */

Inverse-lookup of sine/cosine for an angle (closed loop with costab)

int a = iatan2(1, 1); /* 45° -> 0x2000 (8192) */
int s = isin(a); /* ~ sin(45°) */
int c = icos(a); /* ~ cos(45°) */

Floating-point arcsine

float a = fasin(0.5f); /* ~ 0.5236 (30°) */
float b = facos(-1.0f); /* ~ 3.1416 (180°) */

Unit-amplitude integer arcsine

int a = iasin(16384); /* 16384/32767 ~ 0.5 -> about 0x1555 (30°) */