根据第二个的值,位破解到 return 两个值之一

Bit hack to return one of two values depending on the value of the second

假设 x 是位掩码(即除 1 之外的所有位均为 0)并且 y 是位掩码或等于 0。我需要一点破解return x 如果 y 为非零,如果 y 为零,则 return 为零。

这是一种可能的解决方案:取 xy 的以 2 为底的对数(使用 de Bruijn 序列)并将它们相减,将值存储在 d 中。然后 y << d 将 return x 除非 y 一开始是零。

这种方法有两个问题:1) 如果 y 为零,从技术上讲,以 2 为底的对数是未定义的。不确定这是否重要,因为即使 d 是一些垃圾值,如果 y 为零,y << d 仍应 return 为零; 2) 如果 d 是负数,右移运算符不会变成左移运算符(根据 Google 搜索),这意味着我必须包括一些符号检查。

我相信有更简单的方法,但我找不到它,希望能得到一些帮助。

编辑:澄清一下,我正在寻找执行此操作的最快方法。显而易见的 if (y == 0) return 0; else return x 使用 if 语句,因此受到分支预测的不利影响,这就是为什么我要求助于复杂的 base-2 日志解决方案。

static_cast<bool>(y) * x

只要取 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;
}

在常数时间内应该 运行。你当然可以展开循环。

在大多数常见的处理器架构上,最好使用三元运算符:

/* if y != 0, return x, else return 0 */
int select1 (int x, int y)
{
    return y ? x : 0;
}

三元运算符的使用通常不涉及在现代处理器体系结构上使用分支,因为它可以通过使用条件移动(例如在 x86 上)、指令预测(例如在 ARM 上)以无分支方式轻松实现,或 select 指令(例如在某些 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

请注意,这两个指令序列都没有将分支作为值 selection 的一部分,但是 select2() 中的指令序列需要执行更多的指令,并且具有更长的依赖链,与select1().

中的指令序列