log(10.0) can compile but log(0.0) cannot with undefined reference?
For the following C source code:
#include <math.h>
int main(void)
{
double x;
x = log(0.0);
return 0;
}
When I compile with gcc -lm
, I got:
/tmp/ccxxANVH.o: In function `main':
a.c:(.text+0xd): undefined reference to `log'
collect2: error: ld returned 1 exit status
But, if I replace log(0.0)
with log(10.0)
, then it can compile successfully.
I don't quite understand this, since no matter they make mathematical sense or not, they should compile -- there is no syntax error. Could anyone explain this?
Just in case, my gcc -v
output:
Configured with: ../src/configure -v --with-pkgversion='Ubuntu 4.8.2-19ubuntu1' --with-bugurl=file:///usr/share/doc/gcc-4.8/README.Bugs --enable-languages=c,c++,java,go,d,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-4.8 --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/include/c++/4.8 --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --enable-gnu-unique-object --disable-libmudflap --enable-plugin --with-system-zlib --disable-browser-plugin --enable-java-awt=gtk --enable-gtk-cairo --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-4.8-amd64/jre --enable-java-home --with-jvm-root-dir=/usr/lib/jvm/java-1.5.0-gcj-4.8-amd64 --with-jvm-jar-dir=/usr/lib/jvm-exports/java-1.5.0-gcj-4.8-amd64 --with-arch-directory=amd64 --with-ecj-jar=/usr/share/java/eclipse-ecj.jar --enable-objc-gc --enable-multiarch --disable-werror --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
gcc version 4.8.2 (Ubuntu 4.8.2-19ubuntu1)
Note that this question is about constant folding but the suggested duplicate question is about a missing linking library.
Solution 1:
gcc
can use builtin functions in many cases, their documentation says:
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.
so therefore gcc
will not need to link against the math library when using the builtin function but since log(0)
is not defined it probably forcesgcc
to evaluate it at run-time since it has a side effect.
If we look at the draft C99 standard section 7.12.1
Treatment of error conditions in paragraph 4 it says (emphasis mine):
A floating result overflows if the magnitude of the mathematical result is finite but so large that the mathematical result cannot be represented without extraordinary roundoff error in an object of the specified type. If a floating result overflows and default rounding is in effect, or if the mathematical result is an exact infinity from finite arguments (for example log(0.0)), then the function returns the value of the macro HUGE_VAL, HUGE_VALF, or HUGE_VALL according to the return type, with the same sign as the correct value of the function; if the integer expression math_errhandling & MATH_ERRNO is nonzero, the integer expression errno acquires the value ERANGE; if the integer expression math_errhandling & MATH_ERREXCEPT is nonzero, the ‘‘divide-by-zero’’ floating-point exception is raised if the mathematical result is an exact infinity and the ‘‘overflow’’ floating-point exception is raised otherwise.
We can see from a live example using -S
flag to generate assembly and grep log
to filter out calls to log
.
In the case of log(0.0)
the following instruction is generated (see it live):
call log
but in the case of log(10.0)
no call log
instruction is generated, (see it live).
We can usually prevent gcc
from using builtin function by using the -fno-builtin flag which is probably a quicker way to test whether a builtin is being used.
Note that -lm
needs to go after the source file, for example (taken from linked answer) if main.c
required the math library then you would use:
gcc main.c -lm
Solution 2:
The compilation is alright, it's just the linker switch -lm
that is missing.
The second version probably compiles and links because gcc
replaces log(10.0)
with a constant, so no call to the math library is needed. In the second case, the result is mathematically undefined, and evaluation results in a domain error. In that case, the expression cannot be replaced by a constant, since handling of domain errors might be different at run-time.
Quote from the C-standard (draft):
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 evaluation of log(0.0)
either results in returning the value HUGE_VAL
(not NAN
as I claimed before) or a floating point exception.
EDIT: I corrected my answer based on the comments received and added link to the description in the C-standard.