我可以在编译时在c中计算pow(10,x)吗?

时间:2009-06-30 21:13:42

标签: c compile-time-constant

是否有可能在编译时计算pow(10,x)?

我有一个没有浮点支持和慢整数除法的处理器。我正在尝试在编译时执行尽可能多的计算。如果我将xC/pow(10,x)作为参数传递(x和C总是常量整数,但它们是每次调用的不同常量),我可以大大加快一个特定的函数。我想知道我是否可以通过引入一个自动执行1/pow(10,x)的宏而不是强迫程序员计算它来使这些函数调用更不容易出错?

是否有预处理器技巧?我可以强制编译器优化库调用吗?

10 个答案:

答案 0 :(得分:20)

在溢出int(甚至很长)之前,可能的值很少。为了清楚起见,把它变成一张桌子!

编辑:如果你正在使用浮点数(看起来像你),那么不可能在编译时调用pow()函数而不实际编写在make进程中运行的代码并将值输出到一个文件(如头文件),然后编译。

答案 1 :(得分:19)

GCC将在足够高的优化级别(-O1为我完成)执行此操作。例如:

#include <math.h>

int test() {
        double x = pow(10, 4);
        return (int)x;
}

在-O1 -m32编译为:

        .file   "test.c"
        .text
.globl test
        .type   test, @function
test:
        pushl   %ebp
        movl    %esp, %ebp
        movl    $10000, %eax
        popl    %ebp
        ret
        .size   test, .-test
        .ident  "GCC: (Ubuntu 4.3.3-5ubuntu4) 4.3.3"
        .section        .note.GNU-stack,"",@progbits

这也可以在没有强制转换的情况下工作 - 当然,你在那里得到一个浮点加载指令,因为Linux ABI在FPU寄存器中传递了浮点返回值。

答案 2 :(得分:10)

你可以使用Boost.Preprocessor:

http://www.boost.org/doc/libs/1_39_0/libs/preprocessor/doc/index.html

代码:

#include <boost/preprocessor/repeat.hpp>

#define _TIMES_10(z, n, data) * 10
#define POW_10(n) (1 BOOST_PP_REPEAT(n, _TIMES_10, _))

int test[4] = {POW_10(0), POW_10(1), POW_10(2), POW_10(3)};

答案 3 :(得分:8)

您可以将科学记数法用于浮点值,这是C语言的一部分。它看起来像是:

e = 1.602E-19   // == 1.602 * pow(10, -19)

E之前的数字(E可能是大写或小1.602e-19)是小数部分,其中E之后的(带符号)数字序列是指数部分。默认情况下,该数字的类型为double,但如果需要,您可以附加浮点后缀(fFlLfloatlong double

我不建议将此语义打包到宏中:

  1. 它不适用于变量,浮点值等。
  2. 科学记数法更具可读性。

答案 4 :(得分:5)

实际上,通过利用C预处理器,您可以让它为任何真实C pow(10, x)和积分C计算x。请注意,正如@quinmars所说,C允许您使用科学语法来表达数值常量:

#define myexp 1.602E-19   // == 1.602 * pow(10, -19)

用于常量。考虑到这一点,并且有点聪明,我们可以构造一个预处理器宏,它将Cx合并为一个取幂标记:

#define EXP2(a, b) a ## b
#define EXP(a, b) EXP2(a ## e,b)
#define CONSTPOW(C,x) EXP(C, x)

现在可以将其用作常量数值:

const int myint = CONSTPOW(3, 4); // == 30000
const double myfloat = CONSTPOW(M_PI, -2); // == 0.03141592653

答案 5 :(得分:4)

实际上,你有M4这是一种比GCC更强大的预处理器方式。这两者之间的主要区别是GCC不是递归的而M4是。这使得在编译时进行算术(以及更多!)成为可能。下面的代码示例是您想要做的,不是吗?我在单文件源中使它变得笨重;但我通常将M4的宏定义放在单独的文件中并调整我的Makefile规则。这样,你的代码就会被丑陋的侵入式M4定义保存到我在这里完成的C源代码中。

$ cat foo.c
define(M4_POW_AUX, `ifelse($2, 1, $1, `eval($1 * M4_POW_AUX($1, decr($2)))')')dnl
define(M4_POW, `ifelse($2, 0, 1, `M4_POW_AUX($1, $2)')')dnl

#include <stdio.h>

int                     main(void)
{
  printf("2^0 = %d\n", M4_POW(2, 0));
  printf("2^1 = %d\n", M4_POW(2, 1));
  printf("2^4 = %d\n", M4_POW(2, 4));

  return 0;
}

编译此代码示例的命令行使用GCC和M4从标准输入读取的功能。

$ cat foo.c | m4 - | gcc -x c -o m4_pow -
$ ./m4_pow
2^0 = 1
2^1 = 2
2^4 = 16

希望这有帮助!

答案 6 :(得分:3)

GCC的最新版本(大约4.3)通过评估更复杂的常量函数,增加了使用GMP和MPFR进行编译时优化的能力。这种方法使您的代码简单易用,并且信任编译器来完成繁重的工作。

当然,它的功能有限。 Here's a link to the description in the changelog,其中包含此支持的函数列表。 'pow'就是其中之一。

答案 7 :(得分:2)

如果您只需要使用编译时的值,请使用scientific notation,例如1e2 pow(10, 2)

如果要在编译时填充值,然后在运行时中使用它们,那么只需使用查找表,因为{em>可以完全表示<{3}} 双精度

double POW10[] = {1., 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10,
1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22};

你可以从上面的查找表中获得更大的10次幂来快速得到结果,而不需要一次又一次地乘以10,但结果只是一个接近10的幂的值,就像你使用10eX一样用X&gt; 22

double pow10(int x)
{
   if (x > 22)
      return POW10[22] * pow10(x - 22);
   else if (x >= 0)
      return POW10[x];
    else
        return 1/pow10(-x);
}

如果不需要负指数,则可以删除最终分支。

如果内存是约束,您还可以进一步减少查找表的大小。例如,当指数为奇数时,只存储10的偶数幂并乘以10,表格大小现在只有一半。

答案 8 :(得分:1)

不幸的是,您无法使用预处理器来预先计算库调用。如果x是整数,你可以编写自己的函数,但如果它是浮点类型,我没有看到任何好方法。

答案 9 :(得分:0)

bdonlan的重播是现场,但请记住,如果您愿意在自己的自定义预处理器中解析和分析代码,那么您几乎可以执行在编译框中选择的任何优化。在大多数unix版本中,覆盖隐式规则是一项微不足道的任务,这些规则调用编译器在它到达编译器之前调用自己的自定义步骤。