我可以在 c 中编译时计算 pow(10,x) 吗?
-
21-08-2019 - |
题
是否可以在编译时计算 pow(10,x) ?
我有一个不支持浮点且整数除法缓慢的处理器。我试图在编译时执行尽可能多的计算。如果我通过了两项功能,我可以极大地加快一项特定功能的速度 x
和 C/pow(10,x)
作为参数(x 和 C 始终是常量整数,但它们对于每次调用都是不同的常量)。我想知道是否可以通过引入一个执行以下操作的宏来使这些函数调用不易出错 1/pow(10,x)
自动计算,而不是强迫程序员计算?
有预处理器技巧吗?我可以强制编译器优化库调用吗?
解决方案
您可以使用科学计数法来表示浮点值,这是 C 语言的一部分。看起来像这样:
e = 1.602E-19 // == 1.602 * pow(10, -19)
前面的数字 E
( 这 E
可能是大笔的,也可能是小笔的 1.602e-19
) 是小数部分,其中作为后面的(有符号)数字序列 E
是指数部分。默认情况下,号码的类型为 double
, ,但您可以附加浮点后缀(f
, F
, l
或者 L
)如果你需要一个 float
或一个 long double
.
我不建议将此语义打包到宏中:
- 它不适用于变量、浮点值等。
- 科学计数法更具可读性。
其他提示
有你溢出INT之前可能很少值(甚至长)。出于清楚的缘故,使之成为表!
编辑:如果您使用浮点数(看起来,你是),那么没有它不会是可以调用在编译时POW()函数,而实际上写的是,在化妆过程中运行,该值输出到代码一个文件(如一个标题文件),其然后被编译。
GCC将做到这一点在足够高的优化级别(-O1确实对我来说)。例如:
#include <math.h>
int test() {
double x = pow(10, 4);
return (int)x;
}
在编译-m32 -O1为:
.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 /林达/ 1_39_0 /林达/预处理器/ 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预处理器,你可以得到它来计算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规则。这样一来,你的代码是从丑陋的侵入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
希望这有助于!
GCC(约4.3)的最近版本添加到使用GMP和MPFR通过评估更复杂的功能是恒定做一些编译时优化的能力。这种方法让你的代码,简单便携,相信编译器做繁重。
当然,也有限制它能做什么。 下面是在更改日志中的描述,其中包括一个列表的链接的由该支持的功能。 'POW' 是一个他们。
如果你只需要在使用的值的编译时间的,使用的科学记数法的像1E2为pow(10, 2)
如果您想填充在编译时的值,然后在以后使用它们的运行的然后只需使用一个查找表,因为有的仅23种不同的10个是权力< EM>精确表示在双精度
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一而再,再而倍增,但结果只是当你使用10EX喜欢接近10的功率值与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,表的大小现在只有一半。
不幸的是,你不能使用的预处理器预先计算库的调用。如果x是不可或缺的,你可以编写自己的功能,但如果它是一个浮点型我看不出有什么好办法做到这一点。
bdonlan的重播发现上,但请记住,您可以执行几乎您选择在编译箱只要你愿意来分析和自己的定制预处理器分析代码的任何优化。这是在大多数Unix版本的简单的任务来覆盖调用编译器的隐含规则来调用自己的自定义步骤它击中编译器之前。