Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

sqrt is only defined when argument is nonnegative

Tags:

c

gcc

math.h

sqrt

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?

like image 635
countunique Avatar asked Dec 12 '22 09:12

countunique


1 Answers

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.

like image 150
Shafik Yaghmour Avatar answered Dec 26 '22 22:12

Shafik Yaghmour