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

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


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


当前回答

这是一个复杂的问题。x86家族的类似intel的CPU有一个sin()函数的硬件实现,但它是x87 FPU的一部分,不再用于64位模式(使用SSE2寄存器代替)。在这种模式下,使用软件实现。

有几个这样的实现。一个在fdlibm中,在Java中使用。据我所知,glibc实现包含fdlibm的部分,以及IBM贡献的其他部分。

先验函数的软件实现,如sin(),通常使用多项式逼近,通常从泰勒级数获得。

其他回答

正如许多人指出的那样,它依赖于实现。但就我对你的问题的理解而言,你对数学函数的真正软件实现感兴趣,但只是没有找到一个。如果是这样的话,那么你是这样的:

从http://ftp.gnu.org/gnu/glibc/下载glibc源代码 查看位于解包的glibc根\sysdeps\ieee754\dbl-64文件夹中的文件dosincosc 类似地,您可以找到其余数学库的实现,只需查找具有适当名称的文件

您也可以看看扩展名为.tbl的文件,它们的内容只不过是以二进制形式的不同函数的预计算值的巨大表格。这就是为什么实现如此之快:而不是计算他们使用的任何级数的所有系数,他们只是做一个快速查找,这要快得多。顺便说一下,他们确实用裁缝级数来计算正弦和余弦。

我希望这能有所帮助。

如果你想犯罪

 __asm__ __volatile__("fsin" : "=t"(vsin) : "0"(xrads));

如果你想的话,因为

 __asm__ __volatile__("fcos" : "=t"(vcos) : "0"(xrads));

如果你想要根号方根

 __asm__ __volatile__("fsqrt" : "=t"(vsqrt) : "0"(value));

那么,既然机器指令可以做到,为什么还要使用不准确的代码呢?

它们通常在软件中实现,在大多数情况下不会使用相应的硬件(即汇编)调用。然而,正如Jason所指出的,这些是特定于实现的。

请注意,这些软件例程不是编译器源代码的一部分,而是可以在相应的库中找到,例如clib或GNU编译器的glibc。看到http://www.gnu.org/software/libc/manual/html_mono/libc.html三角函数

如果你想要更大的控制权,你应该仔细评估你到底需要什么。一些典型的方法是查找表的插值、程序集调用(通常很慢)或其他近似方案,如Newton-Raphson的平方根。

没有什么比点击源代码,看看人们是如何在常用的库中实际完成它的了;让我们特别看看一个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的回答。