log (10.0) может компилироваться, но log (0.0) не может?

Для следующего исходного кода C :

#include  int main(void) { double x; x = log(0.0); return 0; } 

Когда я компилирую gcc -lm , я получил:

 /tmp/ccxxANVH.o: In function `main': ac:(.text+0xd): undefined reference to `log' collect2: error: ld returned 1 exit status 

Но если я заменил log(0.0) на log(10.0) , то он может скомпилироваться успешно.

Я не совсем понимаю это, поскольку независимо от того, что они делают математический смысл или нет, они должны компилироваться – нет синтаксической ошибки. Может ли кто-нибудь объяснить это?

На всякий случай, мой gcc -v вывод:

 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) 

Обратите внимание, что этот вопрос касается постоянной сгибания, но предлагаемый дублирующий вопрос касается недостающей библиотеки связывания.

    gcc может использовать встроенные функции во многих случаях, их документация говорит:

    Многие из этих функций оптимизированы только в определенных случаях; если они не оптимизированы в конкретном случае, вызывается вызов функции библиотеки.

    поэтому gcc не нужно связывать с математической библиотекой при использовании встроенной функции, но поскольку log(0) не определен, он, вероятно, заставляет gcc оценивать его во время выполнения, так как он имеет побочный эффект.

    Если мы рассмотрим проект стандарта C99, раздел 7.12.1 Обработка условий ошибки в пункте 4 гласит ( акцент мой ):

    Плавающий результат переполняется, если величина математического результата конечна, но настолько велика, что математический результат не может быть представлен без чрезвычайной ошибки округления в объекте указанного типа. Если всплывающий плавающий результат и округление по умолчанию действуют, или если математический результат является точной бесконечностью из конечных аргументов (например, log (0.0)), тогда функция возвращает значение макроса HUGE_VAL, HUGE_VALF или HUGE_VALL в соответствии с тип возврата с тем же знаком, что и правильное значение функции; если целочисленное выражение math_errhandling & MATH_ERRNO отличное от нуля, целочисленное выражение errno приобретает значение ERANGE; если целочисленное выражение math_errhandling & MATH_ERREXCEPT отличное от нуля, возникает исключение с плавающей запятой ” divide-by-zero ”, если математический результат является точной бесконечностью, а исключение «переполнения» с плавающей запятой создается иначе.

    Мы можем видеть из живого примера с использованием флага -S для генерации сборки и grep log для фильтрации вызовов в log .

    В случае log(0.0) генерируется следующая инструкция ( см. Его в прямом эфире ):

     call log 

    но в случае log(10.0) не генерируется инструкция call log ( см. ее в реальном времени ).

    Обычно мы можем запретить использование встроенной функции gcc с помощью флага -fno-builtin, который, вероятно, является более быстрым способом проверить, используется ли встроенный компонент .

    Обратите внимание, что -lm должен идти после исходного файла , например ( взятый из связанного ответа ), если main.c потребовал математическую библиотеку, то вы использовали бы:

      gcc main.c -lm 

    Компиляция в порядке, это просто связующий переключатель -lm который отсутствует.

    Вторая версия, вероятно, компилирует и связывает, потому что gcc заменяет log(10.0) на константу, поэтому вызов в математическую библиотеку не требуется. Во втором случае результат математически неопределен, а оценка приводит к ошибке домена. В этом случае выражение не может быть заменено константой, так как обработка ошибок домена может отличаться во время выполнения.

    Цитата из C-стандарта ( черновик ):

    При ошибке домена функция возвращает значение, определенное реализацией; если целочисленное выражение math_errhandling & MATH_ERRNO отличное от нуля, целочисленное выражение errno приобретает значение EDOM; если целочисленное выражение math_errhandling & MATH_ERREXCEPT отличное от нуля, возникает исключение ” invalid ” с плавающей запятой.

    Поэтому оценка log(0.0) либо приводит к возврату значения HUGE_VAL (не NAN как я утверждал ранее), либо исключение с плавающей запятой.

    EDIT: я исправил свой ответ на основе полученных комментариев и добавил ссылку на описание в стандарте C.

    Давайте будем гением компьютера.