编译好
#include <math.h>
int main(void) {
double i = sqrt(9.0);
}
如果我将9.0更改为-9.0,那么我的编译器(GNU C)会给出关于'sqrt'的未定义引用的错误。
我期待sqrt函数返回NaN或异常。 C库如何仅为非负参数定义sqrt?
答案 0 :(得分:8)
这种情况正在发生,因为gcc
可以在优化过程中使用内置函数在编译时计算某些函数,包括sqrt
,但很多情况下并非所有情况都是如此。如果是这种情况,则无需向sqrt
发出呼叫,因此无需链接libm
。
gcc文档有一个complete list of builtins,它说(强调我的):
为了优化目的,提供了其余功能。
GCC包含标准C库中许多功能的内置版本。即使您指定-fno-builtin选项,前缀为_ builtin 的版本也始终被视为与C库函数具有相同的含义。 (参见C方言选项)其中许多功能仅在某些情况下进行了优化;如果它们在特定情况下未被优化,则会发出对库函数的调用。
如果我使用gcc -S
编译此代码,并在我们使用sqrt(9.0)
时查看发出的程序集( live example ),那么gcc
将使用内置并且根本不会发出对 sqrt 的调用,因为它将在编译时计算它。如果我们更改代码以使用sqrt(-9.0)
,它现在会发出( live example ):
call sqrt
需要与libm链接,修复方法是将-lm
添加到编译选项的末尾。
如果sqrt(-9)
我们有域错误,如果转到C99草案标准部分7.12.7.5
sqrt函数段落< em> 2 说(强调我的):
sqrt函数计算x的非负平方根。 如果出现域错误 参数小于零。
如果我们回到7.12.1
处理错误情况,则说:
[...]在域错误上,该函数返回一个实现定义的值;如果整数表达式 math_errhandling&amp; MATH_ERRNO 非零,即整数表达式 errno获得值 EDOM ;如果整数表达式 math_errhandling&amp; MATH_ERREXCEPT 非零,引发''无效''浮点异常。
因此将设置 errno 和/或将引发浮点异常,这似乎是gcc
会认为效率不高的情况优化。
答案 1 :(得分:3)
很可能,您忘了链接libm
。更改命令行:
gcc -o foo2 foo2.c -lm
我怀疑基于参数的类型,sqrt()
要么扩展为内联指令,要么扩展为库调用。
答案 2 :(得分:0)
负数的平方根是complex number。 C的sqrt()
函数(通过math.h
)不会返回complex
类型,而是double
。将此函数传递为负值时,可以检查errno
是否有错误情况;有关详细信息,请参阅specification:
对于x的有限值< -0,应发生域错误,并返回NaN(如果支持)或实现定义的值。
如果您想使用complex
类型,请改为包含complex.h
并使用csqrt()
。
答案 3 :(得分:0)
//gcc -std=c99 csqrt.c -lm
#include <stdio.h>
#include <complex.h>
#include <math.h>
void print_complex(double complex c){
double i = cimag(c);
printf("%g %c %gi",
creal(c),
i >= 0. ? '+'
: '-',
fabs(i));
}
int main(){
double complex z = csqrt(-9.0);
print_complex(z);//0 + 3i
}