数学函数是 C 标准库的一部分,GCC 只是使用这些函数。如果你想查看源代码,你可以从官方 glibc 网站 下载源代码(针对 GNU C Library 版本,这是其中最常用的之一),或使用在线代码浏览器。例如,这是用于 log()
的代码。
由于你说你不是很懂编程,我怀疑你可能无法理解 GNU C 标准库。它是数十年优化和兼容性调整的结果,代码非常复杂。因此,我建议您看看 musl C 库。 其源代码更简洁,也更有注释。这里有 log()
函数,以及关于数学函数的所有文件。
最后,无论是 GCC 还是 C 库都没有 "有史以来最快的方法" 来计算这些函数。C 库的目标不是提供每个数学函数的最快实现,而是在保持足够快的同时,仍然具备跨多个架构使用的可移植性,因此这些函数仍然非常快,但很可能并不是 "有史以来最快的"。在最好的情况下,一些数学函数甚至可以缩减为单个 CPU 指令,如果 CPU 支持快速内置硬件数学运算,则可以像 Intel x86 的 fsqrt
一样。
k
和f
,使得 x = 2^k * (1+f),
where sqrt(2)/2 < 1+f < sqrt(2) .
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| < 2000
时,n*ln2_hi
总是精确的。
有关实现细节和特殊情况,请参考此链接:link。
像log
这样的函数是数学库的一部分,通常被称为“libm”。标准C库的实现通常带有libm的实现,因此您要查找的内容很可能在glibc中。您可以在glibc中找到log的实现,链接如下:https://code.woboq.org/userspace/glibc/sysdeps/ieee754/dbl-64/e_log.c.html
源代码中有一些注释,可以给您提供有关使用的算法的提示,但没有详细的解释。
当然,还有不同的libm实现-例如,有openlibm和netlib fdlibm。两者的文档都解释了所使用的算法。以下是openlibm中实现log
的方法:https://github.com/JuliaMath/openlibm/blob/master/src/e_log.c
有趣 - 看起来 openlibm 和 fdlibm 中的 log
来自同一源代码。