在算法中我使用这样的常量:
sqrt(3)
pow(M_PI, 2)
不幸的是,C预处理器不够智能,无法预先计算这些常量。是否有任何额外的预处理层可以与GCC或任何其他C编译器一起使用?
我目前已将这两个常量实现为:
#define SQRT3 1.7320508
#define PIPI (M_PI*M_PI)
但我觉得使用像PIPI
这样模糊不清的名字(这也意味着法语中的小便)并不是最好的解决方案。我认为最好写一下:
inline float square(float x) {
return x * x;
}
然而,这对于平方根是不可能的。至少,我可以得到足够的近似值:
inline float sqrt_approx(float z)
{
int val_int = *(int*)&z;
val_int -= 1 << 23;
val_int >>= 1;
val_int += 1 << 29;
return *(float*)&val_int;
}
不幸的是,编译器还不够聪明,无法将sqrt_approx(3)
解释为1.73
有没有更好的方法来处理这些C限制?
我们在2015年,我们有在火星上漫游的漫游者,我们仍然在处理让我们感受到80年代的C编译器。我错了吗?
答案 0 :(得分:2)
如果没有-ffreestanding
等,Gcc会在编译时计算它们,至少在启用优化的情况下。所以不太可能有函数调用。
如果使用-ffreestanding
,如果自制的内联函数变得不够快,我看不到比sqrt
这样的情况手动定义常量更好的方法。 Gcc的const
attribute可能有助于避免重新计算(但我想如果定义可见,Gcc可以单独推断它。)
问题说他们应该由预处理器计算。我没有看到这个的原因,在预处理器中唯一能用浮点常量做的事情就是对它们进行字符串化或连接。如果确实需要,它们也需要硬编码。预处理器也无法调用内联函数。
答案 1 :(得分:1)
我建议使用常量使编译器意识到它。 并为常量选择更明确的名称。
const float SQRT_OF_3 = 1.7320508;
const float PI_SQUARE = M_PI*M_PI;
答案 2 :(得分:1)
因为这些常量是真常量(不要与const
变量混淆)。您希望您的代码在运行时直接使用它们,您当然不希望调用基本无用的sqrt
或pow
函数,因为您已经在编译时知道了结果
如果你想确定没有无用的电话,你应该使用带有C预处理器的宏。而且你没有错,C编译器有时可以让我们感受到80年代的感觉。还有许多其他更现代的编程语言。
然而随着编译器在优化prorgrams方面越来越现代化,编译器也可能内联函数然后在编译时预先计算它们。知道是否可能的唯一方法是测试并查看生成组件。例如,在我的测试程序中:
static inline int twice(int x)
{
return 2*x;
}
int main()
{
int i = twice(2);
i += twice(4);
printf("Result is %d\n", twice(i));
return 0;
}
使用最新的gcc和-Os
进行编译,然后启用:
main:
sub rsp, 40
.seh_stackalloc 40
.seh_endprologue
call __main
lea rcx, .LC0[rip]
mov edx, 24
call printf
xor eax, eax
add rsp, 40
ret
如您所见,结果24是在汇编代码中预先计算的。对于double
类型,由于浮点数不会立即出现在程序集中,因此它不太明显,但是我检查并进行了优化。然后不再需要使用C预处理器来获取常量。但是如果你想要性能,总是检查汇编代码。