在算法中,我使用这样的常量:
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编译器。我错了吗?
如果没有-ffreestanding
等,Gcc会在编译时计算它们,至少在启用优化的情况下是这样。因此不太可能有函数调用。
如果使用-ffreestanding
,如果自制的内联函数不够快,我看不出比为sqrt
这样的情况手动定义常量更好的方法了。Gcc的const
属性可能有助于避免重新计算(但我猜,如果定义可见,Gcc可以自行推断)。
问题是它们应该由预处理器来计算。我看不出有什么原因,在预处理器中处理浮点常量的唯一方法就是字符串化或连接它们。如果真的需要,它们也需要硬编码。预处理器也不能调用内联函数。
因为这些常量是真常量(不要与const
变量混淆)。您希望您的代码在运行时直接使用它们,当然也不希望对sqrt
或pow
函数进行基本上无用的调用,因为您在编译时就已经知道了结果。
如果你想确信没有无用的调用,你应该使用带有C预处理器的宏。你没有错,C编译器有时会让我们觉得自己处于80年代。还有许多其他更现代的编程语言可用。
但是随着编译器在优化程序分配图方面变得越来越现代化,编译器也有可能内联函数,然后在编译时对其进行预计算。知道是否可能的唯一方法是测试并查看生成的程序集。例如在我的测试程序中:
static inline int twice(int x)
{
return 2*x;
}
int main()
{
int i = twice(2);
i += twice(4);
printf("Result is %dn", 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预处理器了。但是,如果您想要性能,始终检查程序集代码。
我建议使用常量,这会让编译器意识到这一点。并为常量选择更明确的名称。
const float SQRT_OF_3 = 1.7320508;
const float PI_SQUARE = M_PI*M_PI;