sqrt仅在参数为非负时定义

时间:2013-11-22 23:18:27

标签: c gcc math.h sqrt

编译好

#include <math.h>

int main(void) {
    double i = sqrt(9.0);
}

如果我将9.0更改为-9.0,那么我的编译器(GNU C)会给出关于'sqrt'的未定义引用的错误。

我期待sqrt函数返回NaN或异常。 C库如何仅为非负参数定义sqrt?

4 个答案:

答案 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
}