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

发布于 2024-07-26 00:48:34 字数 281 浏览 8 评论 0原文

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

我有一个不支持浮点且整数除法缓慢的处理器。 我试图在编译时执行尽可能多的计算。 如果我将 xC/pow(10,x) 作为参数传递(x 和 C 始终是常量整数,但它们是每次调用都有不同的常量)。 我想知道是否可以通过引入一个自动执行 1/pow(10,x) 的宏来减少这些函数调用的错误,而不是强迫程序员计算它?

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

Is it possible to compute pow(10,x) at compile time?

I've got a processor without floating point support and slow integer division. I'm trying to perform as many calculations as possible at compile time. I can dramatically speed up one particular function if I pass both x and C/pow(10,x) as arguments (x and C are always constant integers, but they are different constants for each call). I'm wondering if I can make these function calls less error prone by introducing a macro which does the 1/pow(10,x) automatically, instead of forcing the programmer to calculate it?

Is there a pre-processor trick? Can I force the compiler optimize out the library call?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

春风十里 2024-08-02 00:48:34

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

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

There are very few values possible before you overflow int (or even long). For clarities sake, make it a table!

edit: If you are using floats (looks like you are), then no it's not going to be possible to call the pow() function at compile time without actually writing code that runs in the make process and outputs the values to a file (such as a header file) which is then compiled.

装纯掩盖桑 2024-08-02 00:48:34

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 寄存器中传递浮点返回值。

GCC will do this at a sufficiently high optimization level (-O1 does it for me). For example:

#include <math.h>

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

Compiles at -O1 -m32 to:

        .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

This works without the cast as well - of course, you do get a floating-point load instruction in there, as the Linux ABI passes floating point return values in FPU registers.

凡间太子 2024-08-02 00:48:34

您可以使用 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)};

You can do it with Boost.Preprocessor:

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

Code:

#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)};
最偏执的依靠 2024-08-02 00:48:34

实际上,通过利用 C 预处理器,您可以让它计算任何实 C 和积分 xC pow(10, 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

Actually, by exploiting the C preprocessor, you can get it to compute C pow(10, x) for any real C and integral x. Observe that, as @quinmars noted, C allows you to use scientific syntax to express numerical constants:

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

to be used for constants. With this in mind, and a bit of cleverness, we can construct a preprocessor macro that takes C and x and combine them into an exponentiation token:

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

This can now be used as a constant numerical value:

const int myint = CONSTPOW(3, 4); // == 30000
const double myfloat = CONSTPOW(M_PI, -2); // == 0.03141592653
深爱不及久伴 2024-08-02 00:48:34

您可以使用科学计数法来表示浮点值,这是 C 语言的一部分。 它看起来像这样:

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

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

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

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

You can use the scientific notation for floating point values which is part of the C language. It looks like that:

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

The number before the E ( the E maybe capital or small 1.602e-19) is the fraction part where as the (signed) digit sequence after the E is the exponent part. By default the number is of the type double, but you can attach a floating point suffix (f, F, l or L) if you need a float or a long double.

I would not recommend to pack this semantic into a macro:

  1. It will not work for variables, floating point values, etc.
  2. The scientific notation is more readable.
花海 2024-08-02 00:48:34

事实上,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

希望这有帮助!

Actually, you have M4 which is a pre-processor way more powerful than the GCC’s. A main difference between those two is GCC’s is not recursive whereas M4 is. It makes possible things like doing arithmetic at compile-time (and much more!). The below code sample is what you would like to do, isn’t it? I made it bulky in a one-file source; but I usually put M4's macro definitions in separate files and tune my Makefile rules. This way, your code is kept from ugly intrusive M4 definitions into the C source code I've done here.

$ 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;
}

The command line to compile this code sample uses the ability of GCC and M4 to read from the standard input.

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

Hope this help!

南城追梦 2024-08-02 00:48:34

如果您只需要在编译时使用该值,请使用科学记数法,如 pow(10, 2) 的 1e2

如果您想在编译时填充值,然后在运行时使用它们,则只需使用查找表,因为有 只有 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,表大小现在只有一半。

If you just need to use the value at compile time, use the scientific notation like 1e2 for pow(10, 2)

If you want to populate the values at compile time and then use them later at runtime then simply use a lookup table because there are only 23 different powers of 10 that are exactly representable in double precision

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};

You can get larger powers of 10 at runtime from the above lookup table to quickly get the result without needing to multiply by 10 again and again, but the result is just a value close to a power of 10 like when you use 10eX with 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);
}

If negative exponents is not needed then the final branch can be removed.

You can also reduce the lookup table size further if memory is a constraint. For example by storing only even powers of 10 and multiply by 10 when the exponent is odd, the table size is now only a half.

握住我的手 2024-08-02 00:48:34

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

当然,它能做的事情是有限的。 这是更改日志中的说明的链接,其中包括一个列表受此支持的功能。 “pow”就是其中之一。

Recent versions of GCC ( around 4.3 ) added the ability to use GMP and MPFR to do some compile-time optimizations by evaluating more complex functions that are constant. That approach leaves your code simple and portable, and trust the compiler to do the heavy lifting.

Of course, there are limits to what it can do. Here's a link to the description in the changelog, which includes a list of functions that are supported by this. 'pow' is one them.

娇纵 2024-08-02 00:48:34

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

Unfortunately, you can't use the preprocessor to precalculate library calls. If x is integral you could write your own function, but if it's a floating-point type I don't see any good way to do this.

说不完的你爱 2024-08-02 00:48:34

bdonlan 的重放非常准确,但请记住,只要您愿意在自己的自定义预处理器中解析和分析代码,您就可以执行在编译框中选择的几乎任何优化。 在大多数版本的 unix 中,覆盖调用编译器在调用编译器之前调用您自己的自定义步骤的隐式规则是一项微不足道的任务。

bdonlan's replay is spot on but keep in mind that you can perform nearly any optimization you chose on the compile box provided you are willing to parse and analyze the code in your own custom preprocessor. It is a trivial task in most version of unix to override the implicit rules that call the compiler to call a custom step of your own before it hits the compiler.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文