假设我有两个4位值,ABCD
和abcd
。如何使用按位运算符交织它,使它成为AaBbCcDd
?伪C中的示例:
nibble a = 0b1001;
nibble b = 0b1100;
char c = foo(a,b);
print_bits(c);
// output: 0b11010010
注意:4位仅用于说明,我想用两个32位的整数执行此操作。
答案 0 :(得分:8)
这被称为完美洗牌操作,并在亨利沃伦的第4-2节“洗牌比特”中详细讨论了点击抨击圣经,Hacker's Delight。
假设x
是一个32位整数,其高16位为a
,低16位为b
:
unsigned int x = (a << 16) | b; /* put a and b in place */
以下直截了当的C代码实现了完美的随机播放:
x = (x & 0x0000FF00) << 8 | (x >> 8) & 0x0000FF00 | x & 0xFF0000FF;
x = (x & 0x00F000F0) << 4 | (x >> 4) & 0x00F000F0 | x & 0xF00FF00F;
x = (x & 0x0C0C0C0C) << 2 | (x >> 2) & 0x0C0C0C0C | x & 0xC3C3C3C3;
x = (x & 0x22222222) << 1 | (x >> 1) & 0x22222222 | x & 0x99999999;
他还提供了一种替代形式,在某些CPU上更快,并且(我认为)更清晰和可扩展:
unsigned int t; /* an intermediate, temporary variable */
t = (x ^ (x >> 8)) & 0x0000FF00; x = x ^ t ^ (t << 8);
t = (x ^ (x >> 4)) & 0x00F000F0; x = x ^ t ^ (t << 4);
t = (x ^ (x >> 2)) & 0x0C0C0C0C; x = x ^ t ^ (t << 2);
t = (x ^ (x >> 1)) & 0x22222222; x = x ^ t ^ (t << 1);
我看到你编辑了你的问题,要求从两个32位输入获得64位结果。我不得不考虑如何扩展沃伦的技术。我认为这不会太难,但我必须考虑一下。如果有人想从这里开始并提供64位版本,我很乐意对它们进行投票。
编辑64位
我以直接的方式将第二个解决方案扩展到64位。首先,我将每个常量的长度加倍。然后我在开头添加了一行来交换相邻的双字节并混合它们。在以下4行中,与32位版本几乎相同,第一行交换相邻的字节和混合,第二行下降到半字节,第三行下移到双位,以及单个位的最后一行。
unsigned long long int t; /* an intermediate, temporary variable */
t = (x ^ (x >> 16)) & 0x00000000FFFF0000ull; x = x ^ t ^ (t << 16);
t = (x ^ (x >> 8)) & 0x0000FF000000FF00ull; x = x ^ t ^ (t << 8);
t = (x ^ (x >> 4)) & 0x00F000F000F000F0ull; x = x ^ t ^ (t << 4);
t = (x ^ (x >> 2)) & 0x0C0C0C0C0C0C0C0Cull; x = x ^ t ^ (t << 2);
t = (x ^ (x >> 1)) & 0x2222222222222222ull; x = x ^ t ^ (t << 1);
答案 1 :(得分:4)
来自斯坦福大学“Bit Twiddling Hacks”页面: https://graphics.stanford.edu/~seander/bithacks.html#InterleaveTableObvious
uint32_t x = /*...*/, y = /*...*/;
uint64_t z = 0;
for (int i = 0; i < sizeof(x) * CHAR_BIT; i++) // unroll for more speed...
{
z |= (x & 1U << i) << i | (y & 1U << i) << (i + 1);
}
查看他们提出的不同和更快算法的页面,以实现相同的目标。
答案 2 :(得分:1)
这是一个基于循环的解决方案,希望比这里的其他一些解决方案更具可读性。
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
uint64_t interleave(uint32_t a, uint32_t b) {
uint64_t result = 0;
int i;
for (i = 0; i < 31; i++) {
result |= (a >> (31 - i)) & 1;
result <<= 1;
result |= (b >> (31 - i)) & 1;
result <<= 1;
}
// Skip the last left shift.
result |= (a >> (31 - i)) & 1;
result <<= 1;
result |= (b >> (31 - i)) & 1;
return result;
}
void printBits(uint64_t a) {
int i;
for (i = 0; i < 64; i++)
printf("%lu", (a >> (63 - i)) & 1);
puts("");
}
int main(){
uint32_t a = 0x9;
uint32_t b = 0x6;
uint64_t c = interleave(a,b);
printBits(a);
printBits(b);
printBits(c);
}
答案 3 :(得分:1)
像这样:
#include <limits.h>
typedef unsigned int half;
typedef unsigned long long full;
full mix_bits(half a,half b)
{
full result = 0;
for (int i=0; i<sizeof(half)*CHAR_BIT; i++)
result |= (((a>>i)&1)<<(2*i+1))|(((b>>i)&1)<<(2*i+0));
return result;
}
答案 4 :(得分:1)
我使用了setting a bit at particular index
和checking the bit at particular index
的{{3}}帖子中使用的2个技巧/操作。
以下代码仅使用这两项操作实现。
int a = 0b1001;
int b = 0b1100;
long int c=0;
int index; //To specify index of c
int bit,i;
//Set bits in c from right to left.
for(i=32;i>=0;i--)
{
index=2*i+1; //We have to add the bit in c at this index
//Check a
bit=a&(1<<i); //Checking whether the i-th bit is set in a
if(bit)
c|=1<<index; //Setting bit in c at index
index--;
//Check b
bit=b&(1<<i); //Checking whether the i-th bit is set in b
if(bit)
c|=1<<index; //Setting bit in c at index
}
printf("%ld",c);
输出:210
,即0b11010010