咬黑客返回两个值中的一个取决于第二个值

时间:2016-03-26 22:52:59

标签: c++ c bit-manipulation

假设x是一个位掩码(即除1之外的所有位都是0)并且y是位掩码或等于0.我需要一点hack来返回{ {1}}如果x非零,则y为零时返回零。

这是一个可能的解决方案:取yx的基数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对数解决方案。

3 个答案:

答案 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;
}

那应该在恒定的时间内运行。你当然可以展开循环。