常量表达式的数学函数是否在编译时预先计算?

时间:2010-01-12 14:01:20

标签: c math compile-time

我倾向于使用常数表达式的数学函数来获得方便性和连贯性(即log(x)/log(2)而不是log(x)/0.3...)。由于这些函数实际上并不是语言本身的一部分,它们都没有在math.h中定义(仅声明),是否会在编译时预先计算常量函数,还是在运行时浪费计算?

6 个答案:

答案 0 :(得分:17)

这取决于编译器和优化标志。正如@AndrewyT指出的那样,GCC能够指定哪些函数是常量和纯属的属性,在这种情况下,答案是肯定的,它将内联结果,因为您可以轻松检查:

$ cat constant_call_opt.c 
#include <math.h>

float foo() {
        return log(2);
}

$ gcc -c constant_call_opt.c -S

$ cat constant_call_opt.s 
        .file   "constant_call_opt.c"
        .text
.globl foo
        .type   foo, @function
foo:
        pushl   %ebp
        movl    %esp, %ebp
        subl    $4, %esp
        movl    $0x3f317218, %eax
        movl    %eax, -4(%ebp)
        flds    -4(%ebp)
        leave
        ret
        .size   foo, .-foo
        .ident  "GCC: (Ubuntu 4.3.3-5ubuntu4) 4.3.3"
        .section        .note.GNU-stack,"",@progbits

没有函数调用,只需加载一个常量(0x3f317218 == 0.69314718246459961 == log(2)

虽然我现在没有任何其他编译器可以尝试,我认为你可以在所有主要的C编译器中看到相同的行为,因为它是一个微不足道的优化。

答案 1 :(得分:11)

有些编译器会在编译时对它们进行评估,但不保证这种行为(这样做也可能导致问题)。您需要检查编译器并查看它的作用。

请注意,在许多系统上,log(2)中的宏M_LN2都提供了<math.h>

答案 2 :(得分:3)

它们通常会在运行时计算出来(请参阅其他答案,了解如何内联它们),但我不一定会使用“浪费”这个词,除非它们有很多和/或代码被执行了很多次。

创建一个#defineconst变量来表示值的含义(PILOG_2等)并使用,而不是仅仅输入常量值。相反。

例如:

#define LOG_2 0.30102999566

这不做任何计算,您可以根据您的环境(32位对64位)指定您想要或可以管理的精度值。

答案 3 :(得分:3)

这取决于。如果编译器可以完全像在运行时那样完成数学运算,并且如果执行链接时优化,并且库是以某种中间形式保存的,那么可以这样做。

大多数编译器都没有完成所有这些。

答案 4 :(得分:3)

对于库函数,某些编译器可能会将这些函数实现为内在函数,这意味着编译器对编译时使用常量替换调用的函数了解得足够多。它是否会这样做取决于编译器。在实践中,我经常注意到一些编译器不愿意在编译时预先计算浮点表达式,即使它们不涉及任何函数调用。

在一般情况下,通常它们不会也不能在编译时计算,假设编译器根本不了解这些函数以便能够在编译时计算它们。也许他们有一些突出的运行时副作用?

某些编译器可能具有非标准的编译器相关扩展,允许用户向编译器提供有关函数的附加信息,以便编译器可以更好地优化函数调用,甚至可以确定是否可以替换给定的调用编译时预先计算。例如,GCC编译器将此类函数 attributes (GCC特定的扩展名)支持为constpure。如果在编译时已知参数,则理论上可以用编译时预计算替换对具有const属性的函数的调用。虽然我不能说GCC是否能真正做到这一点。

在C ++中(即使您的问题标记为C语言),计划的新功能是constexpr声明说明符,它具有类似的用途并且应该具有您描述的效果。

答案 5 :(得分:0)

这在运行时发生,因为函数的代码仅在链接步骤(在编译步骤之后发生)时可用。

要优化步骤,请使用在使用之前初始化的全局变量。