更好的是,用于平方或sqrt常量的宏或内联函数

  • 本文关键字:常量 函数 sqrt 用于 更好 c
  • 更新时间 :
  • 英文 :


在算法中,我使用这样的常量:

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变量混淆)。您希望您的代码在运行时直接使用它们,当然也不希望对sqrtpow函数进行基本上无用的调用,因为您在编译时就已经知道了结果。

如果你想确信没有无用的调用,你应该使用带有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;

最新更新