我在哪里可以找到 GCC 源代码中的数学例程?数学函数如何工作?

Where can I find the mathematical routines in GCC's source? How do the math functions work?

晚安。我是一名数学学士,正在学习 log() 和系列。我确实想看看 GCC 如何计算所有这些东西,这对我有很大帮助,里面什么都没有 math.h 我已经读过了。我疯狂地试图找到 GCC 如何使用有史以来最快的方法计算对数和平方根。我下载了源代码,但我找不到数学例程在哪里。

https://github.com/gcc-mirror/gcc

我只是想看看,我根本不是一个好的程序员,我的擅长是数学。

数学函数是 C 标准库的一部分,GCC 只使用它们。如果想看源码,可以去官方glibc website (for the GNU C Library version, which is one of the most used), or use an online code browser. Here's the code for log()下载源码,比如

既然你说你不是一个程序员,我怀疑你会发现 GNU C 标准库是可以理解的。它是几十年的优化和兼容性调整的结果,代码非常复杂。 我建议看一下 musl C Library instead. The source code is much cleaner and more commented. Here's the log() function, and here's all the files regarding mathematical functions

最后,GCC 或 C 库都没有 "the fastest method ever" 来计算此类函数。 C 库的目标不是为每个数学函数提供尽可能快的实现,而是提供足够好的实现,同时仍然具有足够的可移植性以用于多种体系结构,因此它们仍然非常快,但很可能 不是"the fastest ever"。在最好的情况下,如果 CPU 支持快速内置硬件数学运算(如 for example Intel x86 with fsqrt for the square root),一些数学函数甚至可以减少到单个 CPU 指令。

log 等函数是通常称为 "libm" 的数学库的一部分。标准 C 库的实现通常带有 libm 的实现,因此您正在寻找的东西很可能在 glibc 中。您可以在此处找到 glibc 中登录的实现:https://code.woboq.org/userspace/glibc/sysdeps/ieee754/dbl-64/e_log.c.html

源代码中有一些注释可以提示您使用的算法,但没有详细解释。

当然有不同的 libm 实现 - 例如有 openlibm and netlib fdlibm. The documentation of both explain the algorithm used. Here is how log is implemented in openlibm: https://github.com/JuliaMath/openlibm/blob/master/src/e_log.c

(有趣 - openlibm 和 fdlibm 中的 log 似乎来自同一来源)

看看this log implementation

这是来自 fdlibm that has the implementations (following the IEEE-754) 的许多 C 数学函数。

来自实施:

方法

  1. 参数缩减:找到 kf 使得
    x = 2^k * (1+f),
    where sqrt(2)/2 < 1+f < sqrt(2) .
  1. log(1+f) 的近似值。
Let s = f/(2+f) ; based on log(1+f) = log(1+s) - log(1-s)
     = 2s + 2/3 s**3 + 2/5 s**5 + .....,
         = 2s + s*R
  • 我们在[0,0.1716]上使用特殊的Reme算法生成14次多项式来逼近R,这个多项式逼近的最大误差以2**-58.45为界。换句话说,
                 2      4      6      8      10      12      14
    R(z) ~ Lg1*s +Lg2*s +Lg3*s +Lg4*s +Lg5*s  +Lg6*s  +Lg7*s
    (the values of Lg1 to Lg7 are listed in the program)

    |      2          14          |     -58.45
    | Lg1*s +...+Lg7*s    -  R(z) | <= 2 
    |                             |

注意 2s = f - s*f = f - hfsq + s*hfsq,其中 hfsq = f*f/2。为了保证日志在 1ulp 以下的误差,我们通过

计算日志
    log(1+f) = f - s*(f - R)    (if f is not too large)
    log(1+f) = f - (hfsq - s*(hfsq+R)). (better accuracy)
  1. 最后,
     log(x) = k*ln2 + log(1+f).  
            = k*ln2_hi+(f-(hfsq-(s*(hfsq+R)+k*ln2_lo)))
  • 这里ln2被拆成了两个浮点数:
        ln2_hi + ln2_lo,

其中 n*ln2_hi 始终与 |n| < 2000 一致。

真正的实现和特殊情况的解释你可以在这个link.

中查看