我正在尝试编写一个函数来计算一些位标志,同时避免使用分支或条件:
protected virtual void POOrder_RowSelected(PXCache sender, PXRowSelectedEventArgs e, PXRowSelected del)
{
if (e.Row == null) return;
if (del != null) del(sender, e);
PXUIFieldAttribute.SetVisible<YOUR custom FIELD>(sender, e.Row, <Your true condition to show>);
}
不直接计数位0,但只有在未设置位0时才考虑位1-4,无条件地考虑位5,位6-7只能计数一次。
但是,据我所知,布尔&amp;&amp;和||使用短路评估。这意味着它们的使用创建了一个条件分支,正如您在这样的示例中所看到的: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)) ;
}
如果结果是从第一个子表达式进行短路评估,则保证第二个子表达式中的代码不会被执行。
问题的第一部分:我需要做什么吗?由于这些是纯粹的算术运算而没有副作用,编译器是否会创建条件分支?
第二部分:我理解按位布尔运算符不会进行短路评估,但唯一的问题是这些位不能排列。掩蔽第n位的结果是2 ^ n或零。
将(n&amp; 16)等表达式的最佳方法评估为1或0是什么?
答案 0 :(得分:1)
我假设“第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;
}
答案 1 :(得分:0)
我认为将(n&amp; 16)转换为0或1的最简洁方法是使用int(bool(n & 16))
。如果您在算术表达式(例如int
)中使用它们,则可以删除对bool(n & 2) + bool(n & 4)
的强制转换。
对于计数位集的功能,我建议使用popcount内部函数,在gcc上为__builtin_popcount,在MSVC上为__popcnt。以下是我对你所描述的功能的理解,改为使用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
这与您编写的功能并不完全匹配,但希望从这里获得您的想法。
答案 2 :(得分:0)
使表达式(如&n&amp; 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)