是否可以在编译时计算 pow(10,x) ?
我有一个不支持浮点和慢整数除法的处理器。我试图在编译时执行尽可能多的计算。x
如果我同时传递和作为参数,我可以显着加快一个特定函数的速度C/pow(10,x)
(x 和 C 始终是常量整数,但对于每次调用它们都是不同的常量)。我想知道是否可以通过引入一个1/pow(10,x)
自动执行的宏而不是强迫程序员计算它来使这些函数调用更不容易出错?
有预处理器技巧吗?我可以强制编译器优化库调用吗?
是否可以在编译时计算 pow(10,x) ?
我有一个不支持浮点和慢整数除法的处理器。我试图在编译时执行尽可能多的计算。x
如果我同时传递和作为参数,我可以显着加快一个特定函数的速度C/pow(10,x)
(x 和 C 始终是常量整数,但对于每次调用它们都是不同的常量)。我想知道是否可以通过引入一个1/pow(10,x)
自动执行的宏而不是强迫程序员计算它来使这些函数调用更不容易出错?
有预处理器技巧吗?我可以强制编译器优化库调用吗?
在溢出 int (甚至是 long )之前,可能的值很少。为了清楚起见,把它做成一张桌子!
编辑:如果您使用浮点数(看起来像),那么不,如果不实际编写在 make 过程中运行并将值输出到文件的代码,就不可能在编译时调用 pow() 函数(例如头文件),然后编译。
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 寄存器中传递浮点返回值。
你可以用 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)};
您可以将科学记数法用于浮点值,这是 C 语言的一部分。它看起来像这样:
e = 1.602E-19 // == 1.602 * pow(10, -19)
E
(E
可能大写或小)之前的数字1.602e-19
是小数部分,其中(有符号)数字序列E
是指数部分。默认情况下,数字是类型,但如果需要 a或 a ,double
可以附加浮点后缀(f
、或) 。F
l
L
float
long double
我不建议将此语义打包到宏中:
实际上,通过利用 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
实际上,您拥有 M4,它是一种比 GCC 更强大的预处理器。这两者之间的主要区别是 GCC 不是递归的,而 M4 是递归的。它使诸如在编译时进行算术(以及更多!)之类的事情成为可能。下面的代码示例是您想要做的,不是吗?我在一个文件源中使它变得庞大;但我通常将 M4 的宏定义放在单独的文件中并调整我的 Makefile 规则。这样,您的代码就不会进入我在这里完成的 C 源代码中的丑陋侵入性 M4 定义。
$ 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
希望这有帮助!
如果您只需要在编译时使用该值,请使用1e2 之类的科学记数法pow(10, 2)
如果您想在编译时填充这些值,然后在运行时使用它们,那么只需使用查找表,因为只有 23 种不同的 10次方可以精确表示为双精度
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 > 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,表大小现在只有一半。
最新版本的 GCC(大约 4.3)增加了使用 GMP 和 MPFR 通过评估更复杂的常量函数来进行一些编译时优化的能力。这种方法使您的代码简单且可移植,并相信编译器可以完成繁重的工作。
当然,它可以做的事情是有限度的。这是 changelog 中描述的链接,其中包括受此支持的功能列表。“战俘”就是其中之一。
不幸的是,您不能使用预处理器来预先计算库调用。如果 x 是整数,您可以编写自己的函数,但如果它是浮点类型,我看不出有什么好的方法可以做到这一点。
bdonlan 的重播很准确,但请记住,只要您愿意在自己的自定义预处理器中解析和分析代码,您几乎可以在编译框中执行您选择的任何优化。在大多数版本的 unix 中,覆盖调用编译器的隐式规则以在您自己的自定义步骤到达编译器之前调用它是一项微不足道的任务。