自己造 atan2() 的轮子
有时候有的情况下你并没有现成的 atan2() 轮子,或者你的嵌入式环境不支持,或者你写 Shader 的环境不支持,或者你用的编程语言(比如 VB)不支持的时候,你可以通过根据判断 (x, y) 的坐标值所在的象限,使用 atan() 来自己造一个 atan2() 的轮子。
atan2() 主要被用来求解一个二维向量与 x 坐标轴正方向的夹角的角度值,返回值的取值范围是 [-π, π] 区间。而取角度值的思路是使用反正切函数 atan() 计算 y ÷ x 的值的反正切值,但是光用这个值还不够,因为无法判断 x 和 y 的正负。于是,在使用 atan() 的基础上,对 x 和 y 值的正负做一些 if 判定,就可以修出正确的值了。
例子:my_atan2() 的 C 代码实现
并没有什么特别的优化,只是实现了功能。
gcc 编译的时候需要 -lm 链接器参数。
#include<math.h>
#define PI 3.141592653589793238462643
double my_atan2(double y, double x)
{
if (x == 0)
{
if (y > 0) return PI * 0.5;
else if (y < 0) return -PI * 0.5;
else
{
// 如果你的编程语言支持抛出数学异常,此处应当抛出数学异常
// 比如 return y / x; 抛一个除零异常
// C 语言不支持,所以算了,返回 0。
return 0;
}
}
else
{
double tanv = y / x;
if (x > 0) return atan(tanv);
else if (x < 0)
{
if (y >= 0) return atan(tanv) + PI;
else return atan(tanv) - PI;
}
}
}
写个 POC 检验轮子
#include<math.h>
#define PI 3.141592653589793238462643
double my_atan2(double y, double x)
{
if (x == 0)
{
if (y > 0) return PI * 0.5;
else if (y < 0) return -PI * 0.5;
else
{
// 如果你的编程语言支持抛出数学异常,此处应当抛出数学异常
// 比如 return y / x; 抛一个除零异常
// C 语言不支持,所以算了,返回 0。
return 0;
}
}
else
{
double tanv = y / x;
if (x > 0) return atan(tanv);
else if (x < 0)
{
if (y >= 0) return atan(tanv) + PI;
else return atan(tanv) - PI;
}
}
}
#include<stdio.h>
#include<stdlib.h>
int main(int argc, char **argv)
{
double degree;
int should_be_corrected = 0;
// 遍历 720 个角度
for (degree = -360; degree < 360; degree ++)
{
double angle = degree * PI / 180; // 弧度
double length = (double)rand() / RAND_MAX; // 随机的向量长度
double x = cos(angle) * length;
double y = sin(angle) * length;
double crt_atan2_rad = atan2(y, x); // CRT 库的 atan2 求解的弧度值
double my_atan2_rad = my_atan2(y, x); // 自己的轮子求解的弧度值
double crt_atan2_deg = crt_atan2_rad * 180 / PI; // 转换为角度
double my_atan2_deg = my_atan2_rad * 180 / PI;
if (fabs(crt_atan2_deg - my_atan2_deg) >= 0.1) // 判断误差
{
printf("误差大于 1:原始值:%lf, CRT 值:%lf, 轮子值:%lf\n", degree, floor(crt_atan2_deg), floor(my_atan2_deg));
should_be_corrected = 1;
}
}
if (should_be_corrected)
{
printf("检测到误差,应修正代码实现。\n");
}
else
{
printf("检验完毕。\n");
}
return 0;
}
运行后,显示“检验完毕。”
|