【问题标题】:checking for evenness/oddness of an integer efficiently有效地检查整数的偶数/奇数
【发布时间】:2013-06-09 18:59:50
【问题描述】:

我有一个数学函数,它取决于三个变量 {n、a 和 b},它们由下式给出

{a = n+1, b=n} 当 n 为偶数时

{b = n+1, a=n} 当 n 为奇数时

我的函数被调用很多次,有不同的n。有没有一种有效的方法来实现这一点?我在想 switchif 语句从长远来看可能不会那么有效..

编辑:这是一个最小的例子:

void func(int n)
{
    int a, b;
    if(!(n%2))
    {
        a=n+1;
        b=n;
    }
    else
    {
        a=n;
        b=n+1;
    }
    //continue ...
}

【问题讨论】:

  • “我在想 (...)” 这有关系吗?实施和测试这样一个简单的解决方案并了解它是否有效足够需要多长时间?
  • 不管怎样,如果不使用 if,你还能如何实现决策?
  • @Boris n 永远是积极的
  • 我想unsigned int 在这个功能上会更清楚。

标签: c++ function conditional


【解决方案1】:

您可以使用以下内容:

int a = n|1;
int b = (n+1)&~1;

gcc -O3 输出(n 存储在寄存器 eax 中)

mov edx, eax
add eax, 1
or  edx, 1    //a is in edx
and eax, -2   //b is in eax

使用时

int a = n + 1 - (n&1);
int b = n + (n&1);

给出(n 存储在 edx 中)

mov ecx, edx
mov eax, edx
and ecx, 1
or  eax, 1
add edx, ecx

int a = n + !(n&1);
int b = n + (n&1);

给予

mov eax, edx
and eax, 1
mov ecx, eax
xor ecx, 1
lea eax, [eax+edx*2]

第一个变体(至少使用 gcc)比其他变体少一条指令。不过,根据您的编译器,您必须分析您的代码。

【讨论】:

    【解决方案2】:

    只要使用的数制是二进制补码(即-1 == ~0),就可以这样使用:

      int odd = n & 1;
      a = n + !odd;    // Adds one if n is even
      b = n + odd;     // adds one if n is odd. 
    

    此解决方案既避免了% 2,也避免了2 可能比& 1 多一点,同时避免了if。它应该比if 解决方案快得多,并且可能比% 2 好一点。

    如果不知道数字系统是二进制补码(并且数字可以是负数),这将避免 if:

     int odd = !!(n % 2);  // using !! to ensure 0 or 1 value.
     a = n + !odd;
     b = n + odd;
    

    这与我的第一个代码的逻辑相同,但避免依赖二进制补码。虽然我不知道今天生产的不是二进制补码的任何通用处理器(或者甚至在过去 20 年左右一直在生产 - 毫无疑问有人会告诉我我错了,有些流行的模型仍在制造中……)。

    与性能一样,请确保您知道什么是瓶颈,并且只有在代码确实排在前 10 位时才“弄乱”代码。并衡量新代码和旧代码以确保新代码实际上是一种改进。

    【讨论】:

    • 常用子表达式消除:auto p = (n & 1); a = n + !p; b = n + p;
    • @TemplateRex:你为什么要手动完成编译器的工作?
    • 当然,只要这个优化是有效的,任何有价值的编译器都会将n % 2优化成n & 1
    • @riv 这不仅是为了提高效率,也是为了不要重复自己。将(n&1) 放在一个单独的变量中还可以让您记录它,并在以后根据需要将其更改为其他内容(可能是其他布尔表达式)。
    • @MatthieuM。试试看——因为%2 对负数的定义与&1 不同,它必须用一些额外的指令来“修复”负数,与& 1 相比——它不使用除法指令,但它使用了一些额外的代码。如果你想要的只是这个数字是奇数还是偶数,并且有一个尽可能高效。
    【解决方案3】:

    你可以试试这个,但你必须分析它的性能是否更好/更差:

    int *p[] = {&a, &b};
    *p[n % 2] = n + 1;
    *p[1 - (n % 2)] = n;
    

    【讨论】:

    • 您缺少一个间接级别。无论如何,如果我们尝试第二次猜测编译器,为什么不& 1 而不是% 2
    • @H2CO3 固定间接,谢谢。当我发布时,& 1 已经有了答案,真的没有其他原因。无论如何,我认为这几乎是一个“头脑风暴”问题。
    【解决方案4】:

    作为另一种方式:

    int a, b;
    a = b = n;
    {
        int p = n & 1;
        b += p;
        a += p ^ 1;
    }
    

    这里:

    • 批量复制到ab
    • 帮助编译器检测p 的较小范围。
    • & 在一处。

    【讨论】:

      【解决方案5】:

      试试这个没有 if:

      a = n + (n + 1) % 2
      b = n + n % 2
      

      但我不确定性能是否会有所不同。在判断之前测量性能。

      【讨论】:

        【解决方案6】:
        b = n + (n & 1)
        a = n + n + 1 - b
        

        【讨论】:

        • 这不比!(n & 1)复杂吗?
        • 我收到了 7 条针对 a = n + !(n & 1) 的指令和 5 条针对 b = n + n + 1 - a 的指令,但不要认为这真的很重要。
        【解决方案7】:

        以下代码将判断 a 是偶数还是奇数:

        if(a%2==0)
        {
            a = n+1;
            b=n;
        }
        else
        {
            b = n+1;
            a=n;
        }
        

        这称为“模数”或“模数”运算符,并返回左输入除以右输入的余数。如果 a 除以 2 时余数为 0,则 2 必须均匀进入,因此 a 是偶数。否则就很奇怪了。

        【讨论】:

        • 我很确定 OP 已经知道这一点,因为他们在问题中使用了模数。问题更多是关于if ... else 的效率,而不是“我如何判断一个数字是偶数还是奇数?”
        • 我在作者编辑他们的帖子以包含它之前写了那个答案,但是没关系。
        猜你喜欢
        • 2016-04-21
        • 1970-01-01
        • 2010-09-14
        • 1970-01-01
        • 2015-03-03
        • 1970-01-01
        • 2015-05-31
        • 2011-11-12
        相关资源
        最近更新 更多