我试图从glibc源代码中了解此方法:
26 #if LIBM_SVID_COMPAT
27 /* wrapper sqrtf */
28 float
29 __sqrtf (float x)
30 {
31 if (__builtin_expect (isless (x, 0.0f), 0) && _LIB_VERSION != _IEEE_)
32 return __kernel_standard_f (x, x, 126); /* sqrt(negative) */
33
34 return __ieee754_sqrtf (x);
35 }
36 libm_alias_float (__sqrt, sqrt)
37 #endif
Run Code Online (Sandbox Code Playgroud)
据我了解,如果程序没有sqrt从glibc中找到该函数的有效实现,它将从内核调用硬件函数,即特定的机器函数。这是正确的吗?
还有,这libm_alias_float (__sqrt, sqrt)是什么意思?
GNU C库的数学功能在“ glibc”源代码树中完全实现。他们什么都不依赖操作系统内核。在此上下文中,“内核”一词指的是计算内核,这是某些数学算法的核心,您可能想用手工优化的汇编语言编写该部分。
该函数__kernel_standard_f名称错误。它包含用于处理数学函数错误的通用代码。最好将其命名为__math_domain_error。在这种情况下,当其参数为负数时__sqrtf调用__kernel_standard_f;__kernel_standard_f然后将注意设置errno为EDOM,可能会调用SVID matherr回调,并返回NaN。(神秘代码126告诉__kernel_standard_f哪个函数调用它,以及为什么。它的实现__kernel_standard_f在sysdeps/ieee754/k_standardf.c和中sysdeps/ieee754/k_standard.c。)
条件_LIB_VERSION != _IEEE_与是否启用POSIX和/或SVID数学错误处理有关;现代数学算法实际上只希望在结果中查找NaN而不需要设置errno或调用库浪费时间matherr,因此有一种机制可以将后两者关闭。
如果参数__sqrtf是不否定的,则尾部的呼叫__ieee754_sqrtf,其执行平方根的实际计算。glibc源代码树中有此功能的几种替代实现。其通用的仅C版本在中sysdeps/ieee754/flt-32/e_sqrtf.c。(即使按照glibc的标准,实现数学功能的文件的名称也是神秘的。我不必费力去理解它们,我只是做find sysdeps -name '*sqrtf*'任何事情。)此函数还从检测否定参数(也为零,无穷大)的逻辑开始,和NaN)并返回适当的值,但它不会触及errno。如果您想了解用于计算平方根的数学技术,请查看此文件。
如果您find自己运行上述命令,则会发现其他几个名为的文件e_sqrtf.c。这些都在以特定CPU命名的目录中,这些CPU的浮点单元具有对计算平方根的硬件支持,因此例如sysdeps/x86_64/fpu/e_sqrt.c读取
double
__ieee754_sqrt (double x)
{
double res;
asm ("sqrtsd %1, %0" : "=x" (res) : "xm" (x));
return res;
}
Run Code Online (Sandbox Code Playgroud)
因为x86 sqrtsd指令可以完成全部工作。
当为其中一个CPU进行构建时,Glibc的构建过程将选择这些文件之一,而不是通用C版本。这种机制很复杂,没有完整记录,但是glibc手册的“维护”附录,特别是其“源布局”和“移植”部分,对它进行了详尽的介绍。
libm_alias_float (__sqrt, sqrt)安排__sqrtf也有名字sqrtf,但作为弱别名。为了符合标准,这是必需的。如果您需要更多详细信息,请问一个单独的问题。