我一直在研究。net反汇编和GCC源代码,但似乎在任何地方都找不到sin()和其他数学函数的实际实现…他们似乎总是在引用别的东西。

有人能帮我找到他们吗?我觉得不太可能运行C语言的所有硬件都支持三角函数,所以一定有软件算法,对吧?


我知道有几种方法可以计算函数,并且为了好玩,我写了自己的例程来使用泰勒级数计算函数。我很好奇生产语言是如何做到这一点的,因为我的所有实现总是慢几个数量级,尽管我认为我的算法非常聪明(显然不是)。


当前回答

如果你想要一个软件实现,而不是硬件实现,可以在《数值公式》的第5章中找到这个问题的明确答案。我的副本在一个盒子里,所以我不能给出细节,但简短的版本(如果我没记错的话)是你把tan(theta/2)作为你的基本操作,然后从那里计算其他的。计算是用级数近似完成的,但它比泰勒级数收敛得快得多。

抱歉,我没拿到书就想不起来了。

其他回答

它如何做到这一点的本质在于杰拉德·惠特利的《应用数值分析》节选:

当你的软件程序要求计算机获取一个值时 或者,你有没有想过它是如何得到 如果它能计算的最强大的函数是多项式? 它不会在表中查找这些并进行插值!相反, 计算机逼近除多项式以外的所有函数 一个多项式,可以精确地给出值。

上面要提到的几点是,一些算法实际上是从表中插值的,尽管只是在前几次迭代中。还要注意它是如何提到计算机利用近似多项式而没有指定哪种类型的近似多项式。正如本文中其他人指出的那样,在这种情况下,切比雪夫多项式比泰勒多项式更有效。

无论何时这样一个函数被求值,那么在某种程度上很可能有:

内插的值表(用于快速,不准确的应用程序-例如计算机图形) 收敛于期望值的级数的计算——可能不是泰勒级数,更可能是基于像克伦肖-柯蒂斯这样的奇异正交。

如果没有硬件支持,那么编译器可能会使用后一种方法,只发出汇编代码(没有调试符号),而不是使用c库——这让您在调试器中跟踪实际代码变得很棘手。

没有什么比点击源代码,看看人们是如何在常用的库中实际完成它的了;让我们特别看看一个C库实现。我选择了uLibC。

这是sin函数:

http://git.uclibc.org/uClibc/tree/libm/s_sin.c

看起来它处理了一些特殊情况,然后执行一些参数约简,将输入映射到范围[-pi/4,pi/4],(将参数分成两部分,一个大的部分和一个尾巴),然后调用

http://git.uclibc.org/uClibc/tree/libm/k_sin.c

然后作用于这两个部分。 如果没有尾巴,则使用13次多项式生成近似答案。 如果有尾巴,根据sin(x+y) = sin(x) + sin'(x')y的原理,你会得到一个小的修正

像正弦和余弦这样的函数是在微处理器内部的微码中实现的。例如,英特尔芯片就有相应的组装指令。C编译器将生成调用这些汇编指令的代码。(相反,Java编译器不会。Java在软件而不是硬件中计算三角函数,因此运行速度要慢得多。)

芯片不使用泰勒级数来计算三角函数,至少不完全是这样。首先,他们使用CORDIC,但他们也可能使用一个短的泰勒级数来优化CORDIC的结果,或者用于特殊情况,例如在非常小的角度下以相对较高的精度计算正弦。有关更多解释,请参阅StackOverflow的回答。

不要用泰勒级数。切比雪夫多项式更快更准确,正如上面几个人指出的那样。下面是一个实现(最初来自ZX Spectrum ROM): https://albertveli.wordpress.com/2015/01/10/zx-sine/