【问题标题】:log(10.0) can compile but log(0.0) cannot with undefined reference?log(10.0) 可以编译但 log(0.0) 不能使用未定义的引用?
【发布时间】:2014-08-09 06:45:56
【问题描述】:

对于以下C源代码:

#include <math.h>

int main(void)
{
    double          x;

    x = log(0.0);

    return 0;
}

当我使用gcc -lm 编译时,我得到:

/tmp/ccxxANVH.o: In function `main':
a.c:(.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)

请注意,这个问题是关于常量折叠的,但 suggested duplicate question 是关于缺少链接库的。

【问题讨论】:

  • 这可能与不断传播有关,而不是指定-lm
  • @Cornstalks 确实,应该在源文件之后指定-lm
  • 我不同意您的前提,即应该编译未定义的数学表达式。由于 log(0) 是未定义的/单数的,它在程序中的存在只能是编程错误的结果。我没想到编译器会捕捉到这一点,但考虑到它具有功能,而不是缺陷这一事实。
  • @gdalya 它应该返回 -Inf,而不是未定义。见here
  • @LưuVĩnhPhúc 这是两个不同的问题。在最后一行查看我的编辑。

标签: c gcc math.h constantfolding


【解决方案1】:

gcc 在很多情况下可以使用builtin functions,他们的文档说:

其中许多功能仅在某些情况下进行了优化;如果他们 在特定情况下未优化,调用库函数 被发射。

因此gcc 在使用内置函数时不需要链接到数学库,但由于log(0)not defined 它可能会强制gcc 在运行时对其进行评估,因为它有副作用.

如果我们查看第 4 段中的 draft C99 standard 部分 7.12.1 错误条件的处理,它会说(强调我的):

如果数学值的大小会导致浮点结果溢出 结果是有限的,但如此之大,以至于数学结果不能 表示的对象中没有异常舍入误差 指定类型。如果浮动结果溢出并且默认舍入为 实际上,或者如果数学结果是精确的无穷大 有限参数(例如 log(0.0)),则函数返回 宏 HUGE_VAL、HUGE_VALF 或 HUGE_VALL 的值根据 返回类型,与函数的正确值同号; 如果整数表达式 math_errhandling & MATH_ERRNO 非零, 整数表达式 errno 获取值 ERANGE; 如果整数 表达式 math_errhandling & MATH_ERREXCEPT 非零,则 “除零”浮点异常,如果 数学结果是一个精确的无穷大和“溢出” 否则会引发浮点异常。

我们可以从一个使用-S 标志生成程序集和grep log 过滤掉对log 的调用的实时示例中看到。

log(0.0) 的情况下,会生成以下指令(see it live):

call    log

但在log(10.0) 的情况下,不会生成call log 指令,(see it live)。

我们通常可以通过使用-fno-builtin flag 来阻止gcc 使用内置函数,这可能是测试是否正在使用内置函数的更快方法。

请注意-lm needs to go after the source file,例如(取自链接答案)如果main.c 需要数学库,那么您将使用:

 gcc main.c -lm 

【讨论】:

  • 这是一个很好的答案!是的,这正是问题所在。
  • gcc 无法在运行时评估 log(0) 的原因很棘手。标准指定它返回-HUGE_VAL,但它也会导致范围错误作为副作用(例如在errno 中可见),因此它无法消除调用。
  • @TavianBarnes 实际上我也打算添加它。
  • @TavianBarnes:好吧,编译器可以用产生HUGE_VAL并设置errno的代码替换log(0.0),但生成调用可能更简单(因为无论如何它必须能够为非常量参数这样做)。
  • @KeithThompson 我认为他们希望保持内置评估代码简单并避免处理异常等,这不会使运行时代码更加复杂。
【解决方案2】:

编译没问题,只是缺少链接器开关-lm

第二个版本可能会编译和链接,因为gcclog(10.0) 替换为常量,因此不需要调用数学库。在第二种情况下,结果在数学上是不确定的,并且评估会导致域错误。在这种情况下,表达式不能被常量替换,因为在运行时对域错误的处理可能会有所不同。

引用 C 标准 (draft):

发生域错误时,函数返回实现定义的 价值;如果整数表达式 math_errhandling & MATH_ERRNO 是 非零,整数表达式 errno 获取值 EDOM;如果 整数表达式 math_errhandling & MATH_ERREXCEPT 非零,则 引发了“无效”浮点异常。

因此,log(0.0) 的评估要么导致返回值 HUGE_VAL(不是我之前声称的 NAN),要么返回浮点异常。

编辑:我根据收到的 cmets 更正了我的答案,并添加了指向 C 标准中描述的链接。

【讨论】:

  • 感谢您的回答,抱歉@Shafik 比您早一步,所以我不能接受您的回答作为已接受的答案。
  • 没问题。这就是 SO 的用途... :-)
  • 结果是-Inf,而不是NaN。
  • @TavianBarnes:我认为这是定义的实现。标准只是这样说:“对数函数计算 x 的以 e 为底的(自然)对数。如果参数为负,则会发生域错误。如果参数为零,则可能会发生范围错误。”
  • @Axel "...如果数学结果是精确无穷大(例如log(0.0)),则函数根据返回值返回宏HUGE_VAL、HUGE_VALF或HUGE_VALL的值类型,与函数的正确值具有相同的符号"
猜你喜欢
  • 2022-01-05
  • 2016-12-16
  • 1970-01-01
  • 2015-03-22
  • 2022-01-22
  • 2014-02-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多