C非平凡常数

时间:2010-12-27 19:46:43

标签: c

我想用#define在C中制作几个常量来加速计算。其中两个不仅仅是琐碎的数字,其中一个是右移,另一个是权力。 C中的math.h给出了双精度函数pow(),而我需要整数函数,所以我编写了自己的函数ipow,所以我不需要每次都进行投射。

我的问题是:我要制作的#define常量之一是幂,比如说ipow(M, T),其中MT也是#define常量。 ipow是实际代码中的一个函数,所以当我运行代码时,这实际上似乎会减慢速度(每次提到常量时它是否运行ipow?)。但是,当我使用内置的pow函数并执行(int)pow(M,T)时,代码会加速。我很困惑为什么会这样,因为ipowpow函数同样快。

更一般地说,我可以使用#define使用实际代码中的函数来定义常量吗?上面的例子让我感到困惑的是,这是否加速了事情或实际上减慢了事情。

7 个答案:

答案 0 :(得分:1)

(int)pow(M,T)比使用函数ipow更快,因为如果它们正在执行相同的操作,那么ipow速度很快,但调用它的开销(推送参数等)。 )。

另外,是的,如果你以这种方式#defineipow / pow /每次调用的内容;预处理器不知道它在做什么;它基本上是字符串替换。因此,您的常量只是被文本 ipow(M,T)取代,因此每次需要常量时都会计算它。

最后,对于您的情况,解决方案可能是使用全局变量而不是常量的#define常量。这样,你可以在程序开始时计算一次,然后再使用它(不再计算它)。

答案 1 :(得分:1)

您不需要C ++来进行元编程。如果你有一个C99兼容的C编译器和预处理器,你可以使用P99,如下所示

#include "p99_for.h"
#define P00_POW(NAME, I, REC, Y) (NAME) * (REC)
#define IPOW(N, X) (P99_FOR(X, N, P00_POW, P00_NAM, P99_REP(N,)))

例如,IPOW(4, A)会扩展为((A) * ((A) * ((A) * (A))))。您应该注意的唯一事情是

  • N必须(或扩展为)一个没有后缀的普通十进制常量,例如U或L
  • X不应该有副作用 因为它被评估了好几次

答案 2 :(得分:0)

是的,ipow每次提到常量时都会运行。 C预处理器只是用你定义的那个替换所有常量的提及。

编辑:

如果你真的想在编译时计算这些整数,你可以尝试使用Template Metaprogramming.但这需要C ++。

答案 3 :(得分:0)

我不认为这对c pre-possessor是可能的,因为它不支持递归。

(如果使用的是c ++,可以使用模板元编程)

答案 4 :(得分:0)

我怀疑(int)pow(M,T)比使用(int)ipow(M,T)更快,因为编译器具有pow()函数的特殊知识(作为内在函数)。如果给出常量参数,在使用pow()时它完全省略了函数调用,我不会感到惊讶。

但是,由于它没有ipow()的特殊知识,因此它不会做同样的事情,并最终实际调用该函数。

您应该能够通过查看调试器中生成的程序集或让编译器创建程序集列表来验证是否发生了这种情况。如果这是正在发生的事情,并且您的ipow()函数只不过是对pow()的调用(将结果转换为int),您可能会说服您的编译器执行相同的操作ipow()的优化,使其成为内联函数。

答案 5 :(得分:0)

你的ipow并不快,因为它只是一个简单的函数调用。 我也知道标准C库例程和数学函数的编译器优化。

编译器最有可能确定constexpr参数并在编译时计算#define的值。 在内部,它们将被替换为指数恒定的类似物。

#define pow2(x) ( (x) * (x) )
#define pow3(x) ( (x) * (x) * (x) )
#define pow4(x) ( pow2(x) * pow2(x) )
#define pow5(x) ( pow4(x) * (x) )
#define pow6(x) ( pow3(x) * pow3(x) )
...

唯一的解决方法是使用C ++ metta编程来获得更好的运行时性能。

template<class T, T base, T exp>
struct ipow
{
    static const T value = base * ipow<T, base, exp - 1>::value;
};

template<class T, T base>
struct ipow<T, base, 0>
{
    static const T value = 1;
};

您将使用上述结构如下:

ipow<size_t, M, T>::value

答案 6 :(得分:0)

C预处理器不会在编译时评估对c函数(如ipow或pow)的函数调用,它只会替换文本。

预处理器确实具有类似函数的宏的概念,但是这些宏并没有像文本替换那样被“评估”。很可能会认为你可以编写一个类似递归函数的宏来自动乘以一个常数来将它提升到一个幂,但事实上你不能 - 由于宏体的非评估,你不会实际上,当宏引用自身时,会不断地进行递归计算。


对于移位操作,涉及常量的#define和移位运算符将使预处理器替换文本,但在编译期间将对常量表达式求值,因此这是有效的。实际上它在硬件接口中很常见,即#define UART_RXD_READY(1&lt;&lt; 11)或类似的东西