Question

This compiles fine

#include <math.h>

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

If I change 9.0 to -9.0, then my compiler (GNU C) gives an error about an undefined reference to 'sqrt'.

I was expecting the sqrt function to return NaN or an exception. How does the C library only define sqrt for non-negative arguments?

Was it helpful?

Solution

This is happening because gcc can use builtin functions during the optimization process to compute certain functions including sqrt at compile time in many but not all cases. If that is the case it will not need to emit a call to sqrt and therefore will not need to link against libm.

The gcc documents have a complete list of builtins, and it says (emphasis mine):

The remaining functions are provided for optimization purposes.

GCC includes built-in versions of many of the functions in the standard C library. The versions prefixed with _builtin are always treated as having the same meaning as the C library function even if you specify the -fno-builtin option. (see C Dialect Options) Many of these functions are only optimized in certain cases; if they are not optimized in a particular case, a call to the library function is emitted.

If I compile this code with gcc -S and look at the assembly emitted(live example) when we use sqrt(9.0) then gcc will use a builtin and not emit a call to sqrt at all since it will compute it at compile time. If we change the code to use sqrt(-9.0) it will now emit(live example):

call    sqrt

which will require linking with libm, the fix would be to add -lm to the end of your compile options.

In the case of sqrt(-9) we have a domain error, if go to the C99 draft standard section 7.12.7.5 The sqrt functions paragraph 2 says (emphasis mine):

The sqrt functions compute the nonnegative square root of x. A domain error occurs if the argument is less than zero.

If we go back to 7.12.1 Treatment of error conditions it says:

[...] On a domain error, the function returns an implementation-defined value; if the integer expression math_errhandling & MATH_ERRNO is nonzero, the integer expression errno acquires the value EDOM; if the integer expression math_errhandling & MATH_ERREXCEPT is nonzero, the ‘‘invalid’’ floating-point exception is raised.

so either errno will be set and/or a floating point exception will be raised which seems like a case where gcc would decided it is not efficient to optimize.

OTHER TIPS

Most probably, you forgot to link libm. Change your command line:

gcc -o foo2 foo2.c -lm

I suspect that based on the type of argument, sqrt() is either expanded into an inline instruction or into a library call.

The square root of a negative number is a complex number. C's sqrt() function (via math.h) does not return a complex type, but a double. You can check errno for error conditions when passing this function a negative value; refer to the specification for more information:

For finite values of x < -0, a domain error shall occur, and either a NaN (if supported), or an implementation-defined value shall be returned.

If you want to use complex types, include complex.h and use csqrt(), instead.

//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
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top