是否有可能在编译时计算pow(10,x)?
我有一个没有浮点支持和慢整数除法的处理器。我正在尝试在编译时执行尽可能多的计算。如果我将x
和C/pow(10,x)
作为参数传递(x和C总是常量整数,但它们是每次调用的不同常量),我可以大大加快一个特定的函数。我想知道我是否可以通过引入一个自动执行1/pow(10,x)
的宏而不是强迫程序员计算它来使这些函数调用更不容易出错?
是否有预处理器技巧?我可以强制编译器优化库调用吗?
答案 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
,但如果需要,您可以附加浮点后缀(f
,F
,l
或L
) float
或long double
。
我不建议将此语义打包到宏中:
答案 4 :(得分:5)
实际上,通过利用C预处理器,您可以让它为任何真实C pow(10, x)
和积分C
计算x
。请注意,正如@quinmars所说,C允许您使用科学语法来表达数值常量:
#define myexp 1.602E-19 // == 1.602 * pow(10, -19)
用于常量。考虑到这一点,并且有点聪明,我们可以构造一个预处理器宏,它将C
和x
合并为一个取幂标记:
#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版本中,覆盖隐式规则是一项微不足道的任务,这些规则调用编译器在它到达编译器之前调用自己的自定义步骤。