假设x
是一个位掩码(即除1之外的所有位都是0)并且y
是位掩码或等于0.我需要一点hack来返回{ {1}}如果x
非零,则y
为零时返回零。
这是一个可能的解决方案:取y
和x
的基数2对数(使用de Bruijn序列)并减去它们,将值存储在y
中。然后d
将返回y << d
,除非x
为零开始。
这种方法存在两个问题:1)如果y
为零,从技术上讲,基数为2的对数是未定义的。不确定这是否重要,因为即使y
是垃圾值,d
如果y << d
为零,仍应返回零; 2)如果y
为负数,则右移运算符不会成为左移运算符(根据Google搜索),这意味着我必须包含一些符号检查。
我确信有一种更简单的方法,但我找不到它,并希望得到一些帮助。
编辑:澄清一下,我正在寻找最快的方法来做到这一点。显而易见的d
使用if (y == 0) return 0; else return x
语句,因此受到分支预测的不利影响,这就是为什么我采用复杂的base-2对数解决方案。
答案 0 :(得分:4)
static_cast<bool>(y) * x
答案 1 :(得分:4)
在大多数常见的处理器架构中,首选使用三元运算符:
/* if y != 0, return x, else return 0 */
int select1 (int x, int y)
{
return y ? x : 0;
}
使用三元运算符通常不涉及在现代处理器体系结构上使用分支,因为它可以通过使用条件移动(例如,在x86上),指令预测(例如在ARM上)或选择指令(例如在某些GPU上)。
如果不希望或不允许使用三元运算符,并且需要一点点解决方案,则可以(假设平台使用两个整数的补码表示)使用:
/* if y != 0, return x, else return 0 */
int select2 (int x, int y)
{
return (0 - (y != 0)) & x;
}
请注意select2()
可能慢而不是select1()
。示例:如果我为x86-64架构编译上述函数,我的编译器会为select1()
生成此指令序列
test edx, edx
cmovne edx, ecx
mov eax, edx
ret
但select2()
的这个较长的指令序列:
mov r8d, 1
test edx, edx
cmovne edx, r8d
neg edx
and edx, ecx
mov eax, edx
ret
请注意,两个指令序列都不涉及将分支作为值选择的一部分,但与select2()
中的指令序列相比,select1()
中的指令序列需要执行更多指令并且具有更长的依赖关系链。 }。
答案 2 :(得分:0)
只需取y并使用其位来形成一个全1的字符串,如果它是非零的,则将此与x一起使用。执行此操作的愚蠢方式是线性的,但您也可以使用二进制方法(未给出)。
#include <stdio.h>
#include <limits.h>
int foo(int x, int y) {
int z = 0;
for(int z = 1; z < CHAR_BIT * sizeof(int); z ++) {
y |= y << z;
}
return x & y;
}
int main() {
printf("%lx\n", foo(0x1000, 0xdead));
return 0;
}
那应该在恒定的时间内运行。你当然可以展开循环。