反三角查找表 (atantab)
基于预计算查找表的反三角函数模块,提供整数和浮点两种精度。与正向 余弦查找表 (costab) 相对应,共用同一套 16 位定点角度约定,结果可直接喂回 isin()/icos()。
原理
正向表(costab)按角度均匀采样,天然良态。反三角函数则输入一个比值,其中 asin/acos 在 |x|→1 处导数趋于无穷,若直接对输入值均匀采样会在端点附近产生巨大误差。
为保持全程良态,本模块只对导数有界的 atan(t)(t∈[0,1],导数 1/(1+t²)∈[0.5, 1])建表,并用恒等式派生其余函数:
asin(x) = atan2(x, √(1 - x²))acos(x) = atan2(√(1 - x²), x)
这样把奇异性完全转嫁给平方根,查表部分始终良态。
atantab[i] = atan(i / 1024),共 1025 项(最后一项为 atan(1) = π/4 端点,使线性插值到顶无需夹断)。相邻样点间采用线性插值,精度远高于采样间距。
atan2 通过第一象限归约 + 象限还原实现:先比较 |y| 与 |x| 把比值压到 [0,1],查表得到 [0, π/2] 的角度,再根据 x、y 的符号还原到正确象限。
查找表
extern const int16_t iatantab[1025]; /* 整数 atan 表,角度单位 [0, 8192] = [0, π/4] */
extern const float fatantab[1025]; /* 浮点 atan 表,弧度 [0, π/4] */
API
整数反三角函数
int iatan2(int y, int x); /* 返回 16 位定点角度,[-32768, 32767] 表示 [-π, π) */
int iatan(int t); /* t 为 1.10 定点比值(1024 == 1.0),返回 [-8192, 8192] = [-π/4, π/4] */
int iasin(int v); /* v 为单位幅值(32767 == 1.0),返回 16 位定点角度 */
int iacos(int v); /* v 为单位幅值(32767 == 1.0),返回 16 位定点角度 */
角度采用 16 位定点(2π == 65536),与 costab 完全一致,因此 iatan2() 的结果可直接传给 isin()/icos()。iasin(v)/iacos(v) 接受 [-32767, 32767] 的单位幅值(32767 对应 1.0,与 icostab 对齐);整数平方根复用 sqrti()。iatan2(0, 0) 定义为返回 0。
iatan2() 输入为任意整数,32 位中间运算下幅值约 2×10⁶ 以内安全。
浮点反三角函数
float fatan2(float y, float x); /* 返回弧度,[-π, π] */
float fatan(float t); /* |t|>1 截断,返回 [-π/4, π/4] */
float fasin(float x); /* |x|≤1 截断,返回 [-π/2, π/2] */
float facos(float x); /* |x|≤1 截断,返回 [0, π] */
输入为标准浮点,角度以弧度返回。fasin()/facos() 内部用 sqrtf()。
角度对照(与 costab 一致)
| 角度值 | 弧度 | 角度 |
|---|---|---|
| 0x0000 | 0 | 0° |
| 0x4000 | π/2 | 90° |
| 0x8000 | π | 180° |
| 0xC000 | 3π/2 | 270° |
| 0xFFFF | ≈2π | ≈360° |
弧度转 16 位定点角度:x = (int)(radians / (2 * PI) * 65536)
精度
采用 1024 点 + 线性插值,实测最大误差(对比标准库):
| 函数 | 最大误差 |
|---|---|
fatan2 / fatan / fasin / facos | ≈ 5×10⁻⁷ 弧度(约 0.00003°) |
iatan2 / iasin / iacos | ≈ 1.6×10⁻⁴ 弧度(约 0.009°) |
浮点版已达 float 精度极限;整数版的瓶颈落在 16 位角度输出量化(1~2 LSB),表本身已不再是限制。
使用示例
由坐标求方向 角(整数)
int dx = target_x - origin_x;
int dy = target_y - origin_y;
int angle = iatan2(dy, dx); /* 16 位定点角度,可直接传给 isin()/icos() */
反查角度对应的正余弦(与 costab 闭环)
int a = iatan2(1, 1); /* 45° -> 0x2000 (8192) */
int s = isin(a); /* ≈ sin(45°) */
int c = icos(a); /* ≈ cos(45°) */
浮点计算反正弦
float a = fasin(0.5f); /* ≈ 0.5236 (30°) */
float b = facos(-1.0f); /* ≈ 3.1416 (180°) */
单位幅值整数反正弦
int a = iasin(16384); /* 16384/32767 ≈ 0.5 -> 约 0x1555 (30°) */