【问题标题】:Approximating atan without a math library在没有数学库的情况下近似 atan
【发布时间】:2016-02-04 04:21:13
【问题描述】:

我一直在寻找解决这个问题的方法。我已经看到了许多计算任何 -1

我假设我需要添加或减去 pi 的倍数来抵消它?这种思路正确吗?

目前我有:

double my_atan(double x)
{
    return x - (x*x*x)/3 + (x*x*x*x*x)/5;
}

哪个使用taylor series

对于下面的代码,

int x;
for (x=0; x<M_PI*2; x++)
{
    printf("Actual: %f\n",    atan(x));
    printf("Approx: %f\n", my_atan(x));
    printf("\n");
}

它很快失去控制(正如预期的那样,因为它超出了范围):

Actual: 0.000000
Approx: 0.000000

Actual: 0.785398
Approx: 0.866667

Actual: 1.107149
Approx: 5.733333

Actual: 1.249046
Approx: 42.600000

Actual: 1.325818
Approx: 187.466667

Actual: 1.373401
Approx: 588.333333

Actual: 1.405648
Approx: 1489.200000

此处未显示,但当 theta 在适当范围内时,输出相当准确。

所以我的问题是 my_tan 函数究竟需要采取哪些步骤才能支持更广泛的范围?

关注这个已经有一段时间了,所以任何可以提供的指导都将不胜感激

【问题讨论】:

  • 查找 CORDIC 以获得有效算法或将泰勒级数实现为相应 Wikipedia 文章中的大纲(它仅在 |x|
  • Taylor 对此很不利,因为收敛速度和收敛半径都很差。有更好的技术:最好通过搜索发现,但我相信 Newton Raphson 效果很好。
  • 对于单精度实现,请参阅this question
  • arctan 函数的域不是角度,而是它的 (-inf,inf)。习惯使用语法 'atan(x) = theta'

标签: c trigonometry pi math.h


【解决方案1】:

让我完成你的例子并谈谈一些可能有帮助的事情:

#include <stdio.h>
#include <math.h>


double my_atan(double x)
{
    return x - (x*x*x)/3 + (x*x*x*x*x)/5 - (x*x*x*x*x*x*x)/7;
}

int main()
{
    double x;
    for (x=0.0; x<M_PI*2; x+=0.1)
    {
        printf("x: %f\n",    x);
        printf("Actual: %f\n",    atan(x));
        printf("Approx: %f\n", my_atan(x));
        printf("\n");
    }
}

术语int x 是一个整数,您正在逼近大角度。尝试在这里使用双打,您不会收到转换错误。

现在解决您的问题。你使用的泰勒级数只有在你的 |x| 时才有效。

泰勒级数越远离给定点或在您的情况下为零 (0+x),就会变得不准确。

该系列在 pi/4 之前运行良好,即使在那时它也非常不准确,但较大的值会变得非常糟糕。因此,对于较小的角度,它就足够了。

【讨论】:

  • 不应该是 7 而不是 5?
【解决方案2】:

我用于定点库的解决方案是使用Remez algorithm 的极小极大近似。即使在那里,我对三个范围使用了一组不同的系数:0 到 0.5; 0.5 到 0.75 和 0.75 到 1。通过这种细分,我能够获得 1 ULP 准确度。

那么你需要很好的参数缩减来获得范围内的参数。在我的例子中,我对超过 1 的参数使用了一个很好的倒数函数。下面是身份:

atan(-x) == -atan(x)

atan(x) == pi/2 - atan(1/x) // for x > 1

Taylor vs. Remez 近似值here 上有一个nice blog post;在那个站点上还有一个Remez toolkit 用于查找您需要的系数。

【讨论】:

  • 关于问题的好想法 +1。这篇博文没有给人留下深刻的印象,因为它关注的是区间内的绝对误差,而不是类似于ULP 测量的相对误差。
  • @chux,大约在 lolremez 工具包教程进行到一半的时候,有一个“切换到相对错误”lolengine.net/wiki/doc/maths/remez/tutorial-relative-error
  • @DougCurrie 仍然缺少一个身份来保持性能和准确性,就好像x&gt;0.8 泰勒级数方法开始越来越糟糕地收敛(越接近1)。我正在使用atan(x)-atan(y)=atan((x-y)/(1+x.y))my C++ atan,asin
猜你喜欢
  • 2017-09-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-11-29
  • 2010-10-25
  • 2011-08-02
  • 2014-04-23
  • 2013-06-19
相关资源
最近更新 更多