在 int 中测试 4 个字节的零

Test zero for 4 bytes in an int

我是来求技巧的。我有一个 32 位整数(即 4 个字节)。我想为每个字节测试零,如果其中一个为真,则 return 为真。

例如

int c1 = 0x01020304
cout<<test(c1)<<endl; // output false
int c2 = 0x00010203
cout<<test(c2)<<endl; // output true
int c3 = 0xfffefc00
cout<<test(c3)<<endl; // output true

有没有什么技巧可以在最少的 CPU 周期内做到这一点?

您可以通过屏蔽 & 操作中的每个字节并将结果与​​零进行比较来测试它:

bool hasZeroByte(int32_t n) {
    return !(n & 0x000000FF)
        || !(n & 0x0000FF00)
        || !(n & 0x00FF0000)
        || !(n & 0xFF000000);
}

执行此操作的最快方法可能是使用 strnlen,因为大多数编译器都会对此进行优化,以使用低级指令在字符串中查找零字节。

bool hasZeroByte(int32_t n) {
    return strnlen(reinterpret_cast<char *>(&n), 4) < 4;
}

如果你想更明确一点,你可以使用 memchr 函数,它被记录下来完全按照你的要求做:

bool hasZeroByte(int32_t n) {
    return memchr(reinterpret_cast<void *>(&n), 0, 4) != nullptr;
}

对于那些不相信这个答案的人,请随意看一下 glibc implementation of strlen,看看它已经在做其他答案中提到的所有位技巧。

另请参阅:

著名的bithacks页面中有几种方法

bool hasZeroByte(unsigned int v)
{
    return ~((((v & 0x7F7F7F7F) + 0x7F7F7F7F) | v) | 0x7F7F7F7F);
}

bool hasZeroByte = ((v + 0x7efefeff) ^ ~v) & 0x81010100;
if (hasZeroByte) // or may just have 0x80 in the high byte
{
  hasZeroByte = ~((((v & 0x7F7F7F7F) + 0x7F7F7F7F) | v) | 0x7F7F7F7F);
}

以及编译为汇编时可能最紧凑的方式

#define haszero(v) (((v) - 0x01010101UL) & ~(v) & 0x80808080UL)

因为它们是技巧,所以它们很难理解,所以如果你想要清楚,屏蔽掉每个字节并像 dasblinkenlight 的回答中那样检查

Example assembly output on Compiler Explorer