我在哪里可以找到 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
似乎来自同一来源)
这是来自 fdlibm that has the implementations (following the IEEE-754) 的许多 C 数学函数。
来自实施:
方法
- 参数缩减:找到
k
和 f
使得
x = 2^k * (1+f),
where sqrt(2)/2 < 1+f < sqrt(2) .
- 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)
- 最后,
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.
中查看
晚安。我是一名数学学士,正在学习 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
似乎来自同一来源)
这是来自 fdlibm that has the implementations (following the IEEE-754) 的许多 C 数学函数。
来自实施:
方法
- 参数缩减:找到
k
和f
使得
x = 2^k * (1+f),
where sqrt(2)/2 < 1+f < sqrt(2) .
- 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)
- 最后,
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.
中查看