Ale*_*wer 1 c c++ bit-manipulation
假设x是一个位掩码(即除了一个位之外的所有位都是 0)并且y是位掩码或等于 0。我需要一点技巧来返回xify非零并返回零如果y为零。
这是一种可能的解决方案:取xand的以 2 为底的对数y(使用 de Bruijn 序列)并减去它们,将值存储在d. 然后y << d将返回,x除非y一开始为零。
这种方法有两个问题:1)如果y为零,技术上以 2 为底的对数是未定义的。不确定这是否重要,因为即使d是一些垃圾值,y << d如果y为零,仍然应该返回零;2)如果d是负数,则右移运算符不会成为左移运算符(根据谷歌搜索),这意味着我必须包括一些符号检查。
我相信有一种更简单的方法,但我找不到它,希望得到一些帮助。
编辑:澄清一下,我正在寻找最快的方法来做到这一点。显而易见的是if (y == 0) return 0; else return x使用if语句,因此会受到分支预测的不利影响,这就是为什么我求助于复杂的 base-2 日志解决方案。
在最常见的处理器架构上,最好使用三元运算符:
/* if y != 0, return x, else return 0 */
int select1 (int x, int y)
{
return y ? x : 0;
}
Run Code Online (Sandbox Code Playgroud)
三元运算符的使用通常不涉及在现代处理器架构上使用分支,因为它可以通过使用条件移动(例如在 x86 上)、指令预测(例如在 ARM 上)或选择指令以无分支方式轻松实现(例如在某些 GPU 上)。
如果不希望或不允许使用三元运算符,并且需要一种有点复杂的解决方案,则可以(假设平台使用整数的二进制补码表示)使用:
/* if y != 0, return x, else return 0 */
int select2 (int x, int y)
{
return (0 - (y != 0)) & x;
}
Run Code Online (Sandbox Code Playgroud)
请注意,select2()很可能是慢比select1()。示例:如果我为 x86-64 架构编译上述函数,我的编译器会为select1()
test edx, edx
cmovne edx, ecx
mov eax, edx
ret
Run Code Online (Sandbox Code Playgroud)
但是这个更长的指令序列用于select2():
mov r8d, 1
test edx, edx
cmovne edx, r8d
neg edx
and edx, ecx
mov eax, edx
ret
Run Code Online (Sandbox Code Playgroud)
请注意,这两个指令序列都没有将分支作为值选择的一部分,但是与 中的指令序列相比,in 中的指令序列select2()需要执行更多指令并且还具有更长的依赖链select1()。