在计算和分配中使用条件

时间:2013-02-03 20:38:17

标签: c

最近,我开始使用如下表达式:

res += (i + n / i) * !(n % i);

我假设,!(n % i)的值始终为1或0,因此可以直接用于计算,而不是像

那样编写冗长的if语句
if(!(n % i))
    res += (i + n / i);

如果您想知道,这些行是从我编写的函数中获取的,用于计算数字n的正确除数之和:

unsigned int sum_of_divisors(unsigned int n)
{
    unsigned int res = 1;

    unsigned int i;
    for(i = 2; i < sqrt(n); ++i)
            res += (i + n / i) * !(n % i);
    res += i * (i * i == n);

    return res;
}

我的问题是,此代码是否保证按照我的预期行为进行操作?这对此有何影响(乘法与条件跳转)?如果合适,编译器会这样做吗?

编辑:请注意,我并不特别关注实际代码的性能。我只是想知道,出于纯粹的专业兴趣,两者中哪一个会表现得更好,为什么,以及编译器如何处理每个案例。

至于我这样写的原因,它适用于我的大脑:)

很难描述,但我有一个更好的感觉,乘以1或0而不是三元运算符或if语句,至少在某些情况下。

谢谢, 安迪

3 个答案:

答案 0 :(得分:3)

是的,保证编译器的行为符合您的预期。

不,它不会使你的代码更快,除非编译器真的是低质量的。编译器应该大致相等地处理代码的两个版本,并选择它认为最适合执行条件逻辑的方式。

顺便说一下,原则上!运算符是一个条件分支。一些实现(cpu archs)可能有办法优化它而不需要真正的程序计数器分支,但相同的方法适用于大多数条件。

请注意,从优化的角度来看,您的代码可能只有单向“更好”。写作:

res += (i + n / i) * !(n % i);

您已授予编译器在两个代码路径中写入res的权限。形式如下:

if(!(n % i))
    res += (i + n / i);

如果条件为真,编译器只能写入res。如果res是本地的并且其地址没有泄露,编译器可以确定无论如何都可以安全执行多余的写入,但是如果res的地址在函数外部可见,则编译器必须假设其他线程可能能够访问它,并且在抽象机器中不修改res的代码路径不得在生成的代码中修改它(因为它们可能没有锁定安全地修改它所需的锁)。

答案 1 :(得分:2)

我认为“何时优化”并不是一般性的答案。我个人对这个主题的看法是,所有优化都是不成熟的,直到被直接要求执行它。有时我会等待被多次询问(只是为了安全起见某人真的需要它)。

除此之外,第一个陈述也可以写成:

res += (n % i) ? 0 : (i + n / i);

因为它是乘法而且否定使它难以理解。

修改

  

此代码保证按照我希望的方式运行

如果第一部分(你用0或1乘以)有副作用(就像有人做++),它可能会导致细微的错误。就像我说的那样,只需使用最自然的形式,然后再担心速度。

答案 2 :(得分:1)

!运算符将始终求值为0或1,因此您的假设是正确的。

对于性能影响,任何有价值的优化编译器都应该为if语句或三元运算符生成相同的(或等效的,性能方面的)代码。如果有疑问,请检查装配输出(-S开关,如果使用gcc)或基准代码。