4

这编译得很好

#include <math.h>

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

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

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

4

4 回答 4

10

发生这种情况是因为可以在优化过程中gcc使用内置函数sqrt来计算某些函数,包括在许多但不是所有情况下的编译时。如果是这种情况,它将不需要发出对 的调用sqrt,因此不需要链接到libm.

gcc 文档有一个完整的内置列表,上面写着(强调我的):

其余函数用于优化目的

GCC 包括标准 C 库中许多函数的内置版本。即使您指定了 -fno-builtin 选项,带有 _ builtin前缀的版本始终被视为与 C 库函数具有相同的含义。(请参阅 C 方言选项)其中许多功能仅在某些情况下进行了优化;如果它们在特定情况下未优化,则会发出对库函数的调用。

如果我编译这段代码gcc -S并在我们使用时查看发出的程序集(实时示例),sqrt(9.0)那么gcc将使用内置函数并且根本不会发出对sqrt的调用,因为它将在编译时计算它。如果我们更改代码以使用sqrt(-9.0)它现在将发出(现场示例):

call    sqrt

这将需要与 libm 链接,解决方法是添加-lm到编译选项的末尾。

sqrt(-9)我们有域错误的情况下,如果转到 C99 草案标准部分7.12.7.5 sqrt 函数2段说(强调我的):

sqrt 函数计算 x 的非负平方根。如果参数小于零,则会发生域错误

如果我们回到7.12.1 错误条件的处理,它会说:

[...] 出现域错误时,该函数返回一个实现定义的值;如果整数表达式math_errhandling & MATH_ERRNO非零,则整数表达式 errno 获取值EDOM;如果整数表达式math_errhandling & MATH_ERREXCEPT不为零,则引发“无效”浮点异常。

因此将设置errno和/或引发浮点异常,这似乎是一种gcc决定优化效率不高的情况。

于 2013-11-22T23:30:51.727 回答
3

很可能,您忘记了链接libm。更改命令行:

gcc -o foo2 foo2.c -lm

我怀疑根据参数的类型,sqrt()要么扩展为内联指令,要么扩展为库调用。

于 2013-11-22T23:20:23.987 回答
0

负数的平方根是复数。C 的sqrt()函数 (via math.h) 不返回complex类型,而是返回double. 您可以errno在向此函数传递负值时检查错误情况;有关详细信息,请参阅规范

对于 x < -0 的有限值,将发生域错误,并且应返回 NaN(如果支持)或实现定义的值。

如果您想使用complex类型,请改为 includecomplex.h和 use csqrt()

于 2013-11-22T23:24:28.607 回答
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
}
于 2013-11-22T23:57:14.853 回答