C “优化”;i=b?(i“面具):(i“面具”);
我希望能够设置或清除C “优化”;i=b?(i“面具):(i“面具”);,c,bit-manipulation,ternary-operator,C,Bit Manipulation,Ternary Operator,我希望能够设置或清除uintX\u t t的(多个)位 i是一个运行时变量(uintX\u t)。 b是一个运行时变量(uintX\u t),它被限制为0或1 mask是编译时常量 有没有比以下更好的方法: i = b ? (i | mask) : (i & ~mask) 如果可能的话,我希望避免分支。目标是ARM,如果有关系的话。这里的想法是用乘法替换分支,我们可以根据b的值将每边归零: i = (i | (mask * b)) & (~mask | (mask * b));
uintX\u t t
的(多个)位
i
是一个运行时变量(uintX\u t
)。
b
是一个运行时变量(uintX\u t
),它被限制为0
或1
mask
是编译时常量
有没有比以下更好的方法:
i = b ? (i | mask) : (i & ~mask)
如果可能的话,我希望避免分支。目标是ARM,如果有关系的话。这里的想法是用乘法替换分支,我们可以根据b的值将每边归零:
i = (i | (mask * b)) & (~mask | (mask * b));
另一种选择:始终将位设置为0(左部分),并可选择将位设置为1(右部分)
利用
-1u
是设置了所有位的值这一事实:
i = (i & ~mask) | (mask & -b);
或
第二种方法减少了操作数量和代码大小。第一种方法在超标量体系结构上可能会更快,因为有些操作可以并行执行。最可读的方法是分几个步骤执行,这样做不会影响性能,但会提高可读性 与位运算符的情况一样,必须小心隐式类型提升。例如,不小心使用
~
往往会产生隐式的升级错误。(操作符?:
也会通过平衡第二个和第三个操作数来静默提升结果。)
可读、可移植、安全的代码:
uintx_t i = ... ;
uintx_t b = ... ; // 1 or 0
i &= (uintx_t)~mask; // always clear the bit
i |= mask * b; // if b is 1, set the bit, otherwise OR with 0
i=b
或i==b
?@HongOoii=(b?(i | mask):(i&~mask))
我用Java测试过,如果你愿意,我可以用Java发布测试代码,显示它确实有效现在的问题是:性能损失、分支或乘法方面有什么更糟糕?在我的测试中,我使用了i=101(二进制),mask=110(二进制)您可以很容易地发现,添加两个代码并分别运行100万次,然后测量时间。您可以简单地将乘法重写为一个求反和一个按位的乘法,如果乘法速度较慢(通常值得这样重写)这也源自Guilherme的答案,并使用该答案或分布在and@harold你的评论是不是偶然遗漏了什么?它的结尾看起来很奇怪。不,我只是没有清楚地格式化它,我的意思是,或者在上面分发,而-b
和-1u
不是一回事。如果b
是任何有符号的小整数类型,则会暂时以负数结束。与-1u
不同,1u始终是正数。在这种特殊情况下,这不会是一个问题,但在像(mask&-b)@Lundin这样的代码中会是灾难性的。对,如果b
比int
窄,并且平台不使用二的补码,那么我的答案中的代码实际上有问题。在这种情况下,需要一个额外的cast-(unsigned)b
。谢谢,我喜欢这种可读的方法。如果i
是AVR上的PORTA,这会导致GPIO故障吗?还是在编译过程中这两条语句合并为一条?我希望PORTA被标记为volatile以阻止这种情况发生。@chrisdew它会导致GPIO在几个时钟周期内变低。不允许编译器优化代码,因为PORTA
将是一个易失性寄存器。但在某些硬件上,GPIO端口可能需要一些时钟信号来稳定。
i ^= (i ^ -b) & mask;
uintx_t i = ... ;
uintx_t b = ... ; // 1 or 0
i &= (uintx_t)~mask; // always clear the bit
i |= mask * b; // if b is 1, set the bit, otherwise OR with 0