C++ 基于位掩码将所有位与值异或的最快方法?
我有一个有趣的问题,让我寻找一种更有效的做事方式 假设我们有一个二进制值C++ 基于位掩码将所有位与值异或的最快方法?,c++,c,bit-manipulation,xor,C++,C,Bit Manipulation,Xor,我有一个有趣的问题,让我寻找一种更有效的做事方式 假设我们有一个二进制值 (VALUE) 10110001 (MASK) 00110010 ---------------- (AND) 00110000 现在,我需要能够对掩码值中设置的AND值中的任何位进行异或运算,这些位总是从最低位到最高位: (RESULT) AND1(0) xor AND4(1) xor AND5(1) = 0 现在,在纸上,这肯定很快,因为我可以看到掩码中设置了哪些位。在我看来,以编程方式,我需要一直右移掩码,直
(VALUE) 10110001
(MASK) 00110010
----------------
(AND) 00110000
现在,我需要能够对掩码值中设置的AND值中的任何位进行异或运算,这些位总是从最低位到最高位:
(RESULT) AND1(0) xor AND4(1) xor AND5(1) = 0
现在,在纸上,这肯定很快,因为我可以看到掩码中设置了哪些位。在我看来,以编程方式,我需要一直右移掩码,直到找到一个设置位,用一个单独的值对其进行异或,并循环直到整个字节完成
有人能想出一个更快的方法吗?我正在寻找用最少的操作数和存储值来实现这一点的方法。使用0的异或不会改变任何东西,可以应用掩码,然后无条件地将所有位异或在一起,这可以通过并行前缀的方式完成。所以像这样的东西没有经过测试:
x = m & v;
x ^= x >> 16;
x ^= x >> 8;
x ^= x >> 4;
x ^= x >> 2;
x ^= x >> 1;
result = x & 1
您可以根据需要使用更多或更少的步骤,这是针对32位的。如果我理解正确,您可以
result = value & mask
你想对掩码的1位进行异或运算,并得到一个结果。一系列位的异或与计算位数并检查该计数是偶数还是奇数相同。如果是奇数,则XOR为1;如果是偶数,XOR将给出0
count_bits(mask & result) % 2 != 0
掩码和结果可以简化为简单的结果。你不需要再戴上面具。%2!=0可以交替写入为&1
至于如何计算比特数,该网页给出了一些方法
按布赖恩·克尼汉的方式计算比特集
Brian Kernighan的方法经过了尽可能多的迭代
设置位。因此,如果我们有一个32位的字,只有高位集,那么
它将只通过一次循环
如果要使用该实现,可以进一步优化它。如果你想一想,你不需要完整的比特数。您只需要跟踪它们的奇偶性。您可以在每次迭代中翻转c,而不是计算位
unsigned bit_parity(unsigned v) {
unsigned c;
for (c = 0; v; c ^= 1) {
v &= v - 1;
}
}
感谢您的建议。如果我正确理解了这个问题,您需要的是从掩码中设置的值中获取每一位,并计算这些位的异或 首先,请注意,将值与0异或不会改变结果。所以,为了忽略一些位,我们可以将它们视为零 所以,对掩码中的值中设置的位进行异或运算相当于对VALUE&MASK中的位进行异或运算 现在请注意,如果设置位数为偶数,则结果为0,如果设置位数为奇数,则结果为1 这意味着我们要计算设置位的数量。某些体系结构/编译器有快速计算此值的方法。例如,在GCC上,这可以通过u内置的u popcount获得 因此,在GCC上,可以使用以下公式计算:
int set_bits = __builtin_popcount(value & mask);
return set_bits % 2;
如果您希望代码是可移植的,那么这是不行的。但是,中的一条注释表明,某些编译器可以内联std::bitset::count以有效地获得相同的结果。如果在代码主体中使用v&=v-1,则需要注意的一个重要问题是,在进行计数时会将v的值更改为0。使用其他方法时,该值将更改为1的数目。虽然计数逻辑通常被包装为一个函数,但这不再是一个问题,如果需要在代码主体中显示计数逻辑,那么如果再次需要该值,则必须保留v的副本 除了所介绍的其他两种方法外,以下是位旋转黑客的另一个最爱,对于较大的数字,它通常比循环方法具有更好的性能:
/* get the population 1's in the binary representation of a number */
unsigned getn1s (unsigned int v)
{
v = v - ((v >> 1) & 0x55555555);
v = (v & 0x33333333) + ((v >> 2) & 0x33333333);
v = (v + (v >> 4)) & 0x0F0F0F0F;
v = v + (v << 8);
v = v + (v << 16);
return v >> 24;
}
也许我误解了你的意思,但是XOR 0不是一个不可操作的东西。@ChronoTrigger为什么不呢?1 XOR 0=1;0 xor 0=0。你到底想做什么?你能写出一个更清晰的方程吗?你想要值^XOR&MASK?请说得更具体些。如前所述,不清楚您想要完成什么。实际上,您可以通过将循环更改为for c=0来保存另一个操作;vc^=1那么你不需要在最后做&1:这很好,真的,计数1是一个很好的方法,但是为什么在不需要完全计数的情况下做额外的事情来获得位计数呢?
int set_bits = __builtin_popcount(value & mask);
return set_bits % 2;
/* get the population 1's in the binary representation of a number */
unsigned getn1s (unsigned int v)
{
v = v - ((v >> 1) & 0x55555555);
v = (v & 0x33333333) + ((v >> 2) & 0x33333333);
v = (v + (v >> 4)) & 0x0F0F0F0F;
v = v + (v << 8);
v = v + (v << 16);
return v >> 24;
}