在高中和大学数学中,我们学习如何使用三角函数、它们的作用以及解决哪些问题。但是它们总是被呈现给我一个黑盒子。如果你需要计算正弦或余弦,只需在计算器上按下相应按钮即可。这是可以的。
我想知道的是三角函数通常如何实现。
在高中和大学数学中,我们学习如何使用三角函数、它们的作用以及解决哪些问题。但是它们总是被呈现给我一个黑盒子。如果你需要计算正弦或余弦,只需在计算器上按下相应按钮即可。这是可以的。
我想知道的是三角函数通常如何实现。
首先,你需要进行一定程度的范围缩减。三角函数是周期性的,因此需要将参数缩减到标准区间内。起初,可以将角度缩减为0至360度之间。但是通过使用一些恒等式,您会意识到可以使用更少的内容来完成。如果您计算介于0和45度之间的角度的正弦和余弦,那么您可以以此为基础计算所有角度的所有三角函数。
一旦您缩减了参数,大多数芯片都使用CORDIC算法来计算正弦和余弦。你可能会听到人们说计算机使用泰勒级数。这听起来合理,但事实并非如此。CORDIC算法更适合于高效的硬件实现。(软件库可能在不支持三角函数的硬件上使用泰勒级数)。可能还有一些额外的处理,使用CORDIC算法得到相当好的答案,然后再做其他事情来提高精度。
以上还有一些优化。例如,对于非常小的角度theta(以弧度为单位),sin(theta) = theta,以你拥有的所有精度,因此直接返回theta比使用其他算法更有效率。因此,在实践中,有很多特殊情况的逻辑来挤出尽可能多的性能和精度。市场较小的芯片可能不会付出太多的优化工作。
编辑:Jack Ganssle在他的嵌入式系统书籍"The Firmware Handbook"中有一个不错的讨论。
FYI:如果您有精度和性能限制,泰勒级数不应该用于近似数值目的的函数。(请将它们保留到您的微积分课程中。)它们利用了函数在单个点处的analyticity,例如该点上所有导数都存在的事实。它们不一定在感兴趣的区间内收敛。通常,它们在分配函数近似的准确性方面做得很差,以便在评估点附近“完美”;随着你离开这个点,误差通常会急剧上升。而且,如果您有任何非连续导数的函数(例如方波、三角波及其积分),泰勒级数将给出错误的答案。
最好的“简单”解决方案是,在使用最大次数为N的多项式来近似给定函数f(x)的区间x0<x<x1时,使用Chebyshev approximation;请参见Numerical Recipes中的良好讨论。请注意,我链接到的Wolfram文章中的Tj(x)和Tk(x)使用余弦和反余弦,这些都是多项式,在实践中,您使用递归公式来获得系数。再次参见Numerical Recipes。
编辑:维基百科有一篇关于逼近论的文章。他们引用的一些来源(Hart,“计算机逼近”)已经绝版了(并且二手拷贝往往很昂贵),但详细介绍了这方面的内容。(Jack Ganssle在他的通讯The Embedded Muse第39期中提到了这一点。)
编辑2:这里是sin(x)的Taylor和Chebyshev逼近的一些具体误差指标(见下文)。需要注意以下几点:
范围=-pi/2到+pi/2,度数5(3项)
范围=-pi/2到+pi/2,度数7(4项)
范围 = -pi/4 到 +pi/4,度数为3(2项)
范围 = -pi/4 到 +pi/4,度数为5(3项)
范围 = -pi/4 到 +pi/4,度数为7(4项)
查看维基百科文章以了解三角函数。学习实际在代码中实现它们的好地方是Numerical Recipes。
我不是很懂数学,但我对sin、cos和tan“来自”哪里的理解是,它们在处理直角三角形时被观察到。如果你测量一堆不同直角三角形的边长,并将这些点绘制在图表上,就可以得出sin、cos和tan。正如Harper Shelby所指出的那样,这些函数仅仅是直角三角形的属性。
通过了解这些比率与圆形几何的关系,可以获得更复杂的理解,从而导致弧度和所有相关知识的理解。维基百科中都有。
对于计算机而言,幂级数表示法最常用于计算正弦和余弦,并且这些函数也用于其他三角函数的计算。将这些级数展开到大约8项即可计算出所需的值,精度接近于机器epsilon(可以保存的最小非零浮点数)。
CORDIC方法更快,因为它是在硬件上实现的,但主要用于嵌入式系统而不是标准计算机。
对于tan(),sin()和cos()函数,为了简单起见,将重叠的0到2pi + pi / 80域分成81个相等的间隔,其中“锚点”位于pi / 80,3pi / 80,...,161pi / 80。然后计算并存储这些81个锚点的tan(),sin()和cos()。借助三角恒等式,为每个三角函数开发了单个Maclaurin级数函数。任何±无穷大之间的角度都可以提交给三角逼近函数,因为函数首先将输入角度转换为0到2pi域。此翻译开销包含在逼近开销中。
类似的方法也被开发用于atan()、asin()和acos()函数,其中一个重叠的-1.0到1.1域被分成21个相等的间隔,锚点分别为-19/20、-17/20、...、19/20、21/20。然后只存储这21个锚点的atan()值。再次利用反三角恒等式,为atan()函数开发了一个单一的Maclaurin级数函数。然后使用atan()函数的结果来近似asin()和acos()。
由于所有反三角近似函数都基于atan()近似函数,因此允许任何双精度参数输入值。但是,asin()和acos()近似函数的参数输入被截断为±1域,因为任何超出该域的值都是无意义的。
为了测试近似函数,强制评估了十亿个随机函数评估(即,不允许-O3优化编译器绕过评估某些计算结果不会被使用的内容)。为了消除评估十亿个随机数并处理结果的偏差,首先执行了一次没有评估任何三角或反三角函数的运行成本。然后从每个测试中减去这个偏差,以获得更具代表性的实际函数评估时间的近似值。
表2. 执行指定函数或函数10亿次所花费的时间(以秒为单位)。这些估计值是通过从表1的第一行显示的评估10亿个随机数的时间成本中减去表1中其余行得到的。
tan()所花费的时间:18.0515 18.2545
TAN3()所花费的时间:5.93853 6.02349
TAN4()所花费的时间:6.72216 6.99134
sin()和cos()所花费的时间:19.4052 19.4311
SINCOS3()所花费的时间:7.85564 7.92844
SINCOS4()所花费的时间:9.36672 9.57946
atan()所花费的时间:15.7160 15.6599
ATAN1()所花费的时间:6.47800 6.55230
ATAN2()所花费的时间:7.26730 7.24885
ATAN3()所花费的时间:8.15299 8.21284
asin()和acos()所花费的时间:36.8833 36.9496
ASINCOS1()所花费的时间:10.1655 9.78479
ASINCOS2()所花费的时间:10.6236 10.6000
ASINCOS3()所花费的时间:12.8430 12.0707
(为了节省空间,表格1未显示。) 表格2展示了两次独立运行每个近似函数十亿次评估的结果。第一列是第一次运行的结果,第二列是第二次运行的结果。函数名称中的数字“1”、“2”、“3”或“4”表示在Maclaurin级数函数中使用的项数,以评估特定的三角或反三角近似值。SINCOS#()表示同时计算sin和cos。同样,ASINCOS#()表示同时计算asin和acos。同时计算两个量几乎没有额外的开销。
结果显示,略微增加项数会略微增加执行时间,这是可以预料的。即使是最小的项数也可以在除tan()近似值接近±无穷大的地方以外的任何地方给出约12-14位的精度。人们期望即使是tan()函数在那里也会有问题。
在 Unix 上的高端 MacBook Pro 笔记本电脑和 Linux 上的高端台式计算机上获得了类似的结果。