我一直在研究 .NET 反汇编和 GCC 源代码,但似乎无法在任何地方找到实际实现sin()和其他数学函数……它们似乎总是在引用其他东西。
sin()
谁能帮我找到他们?我觉得 C 将运行的所有硬件都不太可能支持硬件中的三角函数,所以 某处 必须有软件算法,对吧?
我知道 可以 计算函数的几种方法,并且已经编写了我自己的例程来使用泰勒级数来计算函数以获取乐趣。我很好奇真实的生产语言是如何做到的,因为我所有的实现总是慢几个数量级,尽管我认为我的算法非常聪明(显然它们不是)。
在 GNU libm 中, 的实现sin依赖于系统。因此,您可以在sysdeps的相应子目录中找到每个平台的实现。
sin
一个目录包括 IBM 提供的 C 语言实现。sin()自 2011 年 10 月以来,这是您在典型的 x86-64 Linux 系统上调用时实际运行的代码。它显然比fsin汇编指令快。源代码:sysdeps/ieee754/dbl-64/s_sin.c,查找__sin (double x).
fsin
__sin (double x)
这段代码非常复杂。 没有一种软件算法在整个x 值范围内尽可能快且准确,因此该库实现了几种不同的算法,其首要任务是查看 x 并决定使用哪种算法。
当 x 非常 非常 接近 0 时,sin(x) == x是正确答案。
sin(x) == x
稍远一点,sin(x)使用熟悉的泰勒级数。但是,这仅在 0 附近准确,所以…
sin(x)
当角度大于约 7° 时,使用不同的算法,计算 sin(x) 和 cos(x) 的泰勒级数近似值,然后使用预先计算的表中的值来改进近似值。
什么时候 | x | > 2,上述算法都不起作用,因此代码首先计算一些接近 0 的值,该值可以输入sin或cos代替。
cos
还有另一个分支来处理 x 是 NaN 或无穷大。
这段代码使用了一些我以前从未见过的数字技巧,尽管据我所知,它们在浮点专家中可能是众所周知的。有时几行代码需要几段来解释。例如,这两行
double t = (x * hpinv + toint); double xn = t - toint;
用于(有时)将 x 减少到接近 0 的值,该值与 x 相差蟺/2 的倍数,特别是xn脳 蟺/2。不进行划分或分支的方式非常聪明。但是一点评论都没有!
xn
较旧的 32 位版本的 GCC/glibc 使用了该fsin指令,这对于某些输入来说出人意料地不准确。有一篇引人入胜的博客文章仅用 2 行代码说明了这一点。
fdlibmsin在纯 C 中的实现比 glibc 简单得多,并且得到了很好的评论。源代码:fdlibm/s_sin.c和fdlibm/k_sin.c