【问题标题】:Where can I find the mathematical routines in GCC's source? How do the math functions work?我在哪里可以找到 GCC 源代码中的数学例程?数学函数是如何工作的?
【发布时间】:2020-04-10 03:43:58
【问题描述】:

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

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

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

【问题讨论】:

  • 它在 C 库中 - 请参阅此处stackoverflow.com/questions/11550140/…
  • GCC 编译器本身使用库 GMP、MFPR、MPC 来进行计算(等等)。编译器生成的代码使用您机器上的 C 库(库)——通常是 -lm 来链接数学库,但有时不需要(例如在 Mac 上)。

标签: c math gcc discrete-mathematics


【解决方案1】:

数学函数是 C 标准库的一部分,而 GCC 只是使用这些函数。如果你想看源代码,你可以从官方glibc website下载源代码(对于GNU C库版本,这是最常用的版本之一),或者使用an online code browser。以the code for log() 为例。

虽然你说你不是一个程序员,但我怀疑你会发现 GNU C 标准库是可以理解的。是几十年优化和兼容性调整的结果,代码非常复杂。 我建议改为查看musl C Library。源代码更干净,注释也更多。 Here's the log() function,这里是all the files regarding mathematical functions

最后,GCC 或 C 库都没有“有史以来最快的方法”来计算这些函数。 C 库的目标不是为每个数学函数提供尽可能快的实现,而是提供足够好的实现,同时仍然具有足够的可移植性以在多种架构上使用,所以这些仍然非常快,但很可能 不是“有史以来最快的”。在最好的情况下,如果 CPU 支持快速的内置硬件数学运算(如 for example Intel x86 with fsqrt for the square root),则某些数学函数甚至可以简化为一条 CPU 指令。

【讨论】:

    【解决方案2】:

    看看this log implementation

    这是来自fdlibm,它实现了(在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有错误,我们计算log的方式是

        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| &lt; 2000 完全一致。

    真实实现和特殊情况的解释你可以在这个link查看。

    【讨论】:

      【解决方案3】:

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

      源代码中有一些 cmets 提供了有关所使用算法的提示,但没有详细解释。

      当然,libm 有不同的实现方式——例如有openlibmnetlib fdlibm。两者的文档都解释了使用的算法。以下是log 在 openlibm 中的实现方式:https://github.com/JuliaMath/openlibm/blob/master/src/e_log.c

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-02-20
        • 1970-01-01
        • 2018-12-26
        相关资源
        最近更新 更多