我有一个有趣的问题,让我寻找一种更有效的做事方式。
假设我们有一个值(二进制)
(VALUE) 10110001
(MASK) 00110010
----------------
(AND) 00110000
现在,我需要能够对(AND)
值中设置的(MASK)
值中的任何位进行异或运算(始终从最低位到最高位):
(RESULT) AND1(0) xor AND4(1) xor AND5(1) = 0
现在,在纸面上,这肯定很快,因为我可以看到掩码中设置了哪些位。在我看来,在程序上我需要保持正确移位MASK,直到我找到一个设置位,将其与一个单独的值进行异或,并循环直到整个字节完成。
有人能想到更快的方式吗?我正在寻找使用最少数量的操作和存储值来实现此目的的方法。
答案 0 :(得分:5)
如果我理解你,你有
result = value & mask
并且您希望将mask & result
的1位异或。一系列位的XOR与计数位数和检查该计数是偶数还是奇数相同。如果它是奇数,则XOR为1;如果均匀,XOR将给出0。
count_bits(mask & result) % 2 != 0
mask & result
可以简化为result
。您不需要再次与mask
进行AND运算。 % 2 != 0
可以交替写为& 1
。
count_bits(result) & 1
至于如何计算位数,Bit Twiddling Hacks网页会提供一些bit counting algorithms。
计数位设置,Brian Kernighan的方式
unsigned int v; // count the number of bits set in v unsigned int c; // c accumulates the total bits set in v for (c = 0; v; c++) { v &= v - 1; // clear the least significant bit set }
Brian Kernighan的方法经历了尽可能多的迭代 设置位。因此,如果我们有一个只有高位设置的32位字,那么 它只会循环一次。
如果您要使用该实现,则可以进一步优化它。如果你考虑一下,你就不需要完整的位数。您只需要跟踪他们的奇偶校验。不是计算位数,而是每次迭代都可以翻转c
。
unsigned bit_parity(unsigned v) {
unsigned c;
for (c = 0; v; c ^= 1) {
v &= v - 1;
}
}
(感谢Slava提出建议。)
答案 1 :(得分:5)
如果我正确理解了这个问题,你想要的是从MASK中设置的VALUE中获取每一位,并计算这些位的XOR。
首先,请注意,将值设为0并不会改变结果。因此,要忽略某些位,我们可以将它们视为零。
因此,对VALUE中设置的MASK中的位进行异或运算相当于对VALUE& MASK中的位进行异或运算。
现在请注意,如果设置位数是偶数,则结果为0,如果是奇数,则结果为1.
这意味着我们想要计算设置位数。某些体系结构/编译器可以快速计算此值。例如,在GCC上,可以使用__builtin_popcount
获得。
所以在GCC上,这可以通过以下方式计算:
int set_bits = __builtin_popcount(value & mask);
return set_bits % 2;
如果您希望代码可移植,那么这不会做。但是,this answer中的评论表明某些编译器可以内联std::bitset::count
来有效地获得相同的结果。
答案 2 :(得分:1)
使用带有0的XOR不会改变任何东西,可以应用掩码,然后无条件地将所有位异或,这可以以并行前缀方式完成。所以像这样(未经测试):
x = m & v;
x ^= x >> 16;
x ^= x >> 8;
x ^= x >> 4;
x ^= x >> 2;
x ^= x >> 1;
result = x & 1
您可以根据需要使用更多(或更少)步骤,这是32位。
答案 3 :(得分:0)
如果在代码正文中使用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;
}