使用逻辑和/或无条件/分支

Use of logical AND/OR without conditional/branching

本文关键字:无条件 分支 逻辑和      更新时间:2023-10-16

我正在尝试编写一个函数,该函数在避免使用分支或条件的同时计算一些位标志:

uint8_t count_descriptors(uint8_t n)
{
    return 
    ((n & 2)   && !(n & 1))   +
    ((n & 4)   && !(n & 1))   +
    ((n & 8)   && !(n & 1))   +
    ((n & 16)  && !(n & 1))   +
    ((n & 32)  && 1       )   +
    ((n & 64)  || (n & 128))  ;
}

位零不是直接计数的,但是仅在未设置位0时才考虑位1-4,位5被视为无条件,位6-7只能计数一次。

但是,我知道布尔值&&和||使用短路评估。这意味着他们在此类示例中看到的那样,它们会创建一个条件分支:如果从第一个子表达评估结果短路,则不会执行第二个子表达中的代码的if( ptr != nullptr && ptr->predicate())

。 。

问题的第一部分:我需要做任何事情吗?由于这些是纯粹的算术操作,没有副作用,因此编译器会创建条件分支吗?

第二部分:我知道位于布尔运算符不会简短评估,但是钻头不排队的唯一问题。掩盖n位的结果为2^n或零。

制作诸如(N& 16)等表达式的最佳方法是什么?

我假设"只有"位6-7"才能计数一次。

在这种情况下,类似的东西应该起作用

uint8_t count_descriptors(uint8_t n)
{
    uint8_t retVar;
    retVar = (n&1)*(n&2 >> 1) + 
             (n&1)*(n&4 >> 2) + 
             (n&1)*(n&8 >> 3) +
             (n&1)*(n&16 >> 4) + 
             (n&32 >> 5) + 
             (int)((n&64 >> 6) + (n&128 >> 7) >= 1)
    return retVar;
}

制作诸如(N& 16)等表达方式的最佳方法是什么 到1或0?

通过正确移动所需的位数:(n>>4)&1(n&16)>>4

我可能会使用一个查找表,要么适用于所有256个值,或者至少适用于4的组。

nbits[16]={0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4};
//count bits 1..4 iff bit 0 is 0, bit 5 always, and bit 6 or 7
return (!(n&1) * nbits[(n>>1)&0xF]) + ((n>>5)&1) + (((n>>6)|(n>>7))&1)

我认为将(N& 16)转换为0或1的最干净的方法就是仅使用int(bool(n & 16))。如果您在算术表达式中使用了int的铸件(例如bool(n & 2) + bool(n & 4))。
对于您的计数位设置的功能,我建议使用GCC上的__builtin_popcount和MSVC上的__popcnt使用popcount intinsic函数。以下是我对您描述的功能的理解,更改为使用Popcount。

f(n & 1)
{
  //clear out first 4 bits and anything > 255
  n &= 0xF0;
}
else
{
  //clear out anything > 255 and bottom bit is already clear
  n &= 0xFF;
}
return __builtin_popcount(n); //intrinsic function to count the set bits in a number

这与您写的功能不太匹配,但希望从这里您得到这个想法。