找回密码
 立即注册→加入我们

QQ登录

只需一步,快速开始

搜索
热搜: 下载 VB C 实现 编写
技术宅的结界 门户 查看主题

【C】自己造 atan2() 的轮子

发布者: 0xAA55 | 发布时间: 2023-5-2 15:23| 查看数: 1191| 评论数: 5|帖子模式

欢迎访问技术宅的结界,请注册或者登录吧。

您需要 登录 才可以下载或查看,没有账号?立即注册→加入我们

×

自己造 atan2() 的轮子

有时候有的情况下你并没有现成的 atan2() 轮子,或者你的嵌入式环境不支持,或者你写 Shader 的环境不支持,或者你用的编程语言(比如 VB)不支持的时候,你可以通过根据判断 (x, y) 的坐标值所在的象限,使用 atan() 来自己造一个 atan2() 的轮子。

atan2()主要被用来求解一个二维向量与 x 坐标轴正方向的夹角的角度值,返回值的取值范围是 [-π, π] 区间。而取角度值的思路是使用反正切函数 atan() 计算 y ÷ x 的值的反正切值,但是光用这个值还不够,因为无法判断 x 和 y 的正负。于是,在使用 atan() 的基础上,对 x 和 y 值的正负做一些 if 判定,就可以修出正确的值了。

285px-math-atan2.png

例子: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;
}

运行后,显示“检验完毕。”

最新评论

YY菌 发表于 2023-5-9 09:03:31
我觉得x和y都为0的时候不应该返回0,而是返回nan来表示错误。
usr 发表于 2023-5-9 14:45:28
YY菌 发表于 2023-5-9 09:03
我觉得x和y都为0的时候不应该返回0,而是返回nan来表示错误。

返回0/0触发异常试试?
YY菌 发表于 2023-5-10 09:32:52
usr 发表于 2023-5-9 14:45
返回0/0触发异常试试?

你那是整数,浮点数 0.0 / 0.0 是不会有异常的,计算结果是 nan 值。
美俪女神 发表于 2023-5-17 08:54:06
我的这些知识都还给老师了。。。
0xAA55 发表于 2023-5-22 09:22:04
YY菌 发表于 2023-5-10 09:32
你那是整数,浮点数 0.0 / 0.0 是不会有异常的,计算结果是 nan 值。

啊?是吗?我大意了

QQ|Archiver|小黑屋|技术宅的结界 ( 滇ICP备16008837号 )|网站地图

GMT+8, 2024-4-25 22:08 , Processed in 0.044002 second(s), 40 queries , Gzip On.

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表