简体   繁体   English

如何使用按位运算符交错2个布尔值?

[英]How to interleave 2 booleans using bitwise operators?

Suppose I have two 4-bit values, ABCD and abcd . 假设我有两个4位值, ABCDabcd How to interleave it, so it becomes AaBbCcDd , using bitwise operators? 如何交织它,使用按位运算符变成AaBbCcDd Example in pseudo-C: 伪C中的示例:

nibble a = 0b1001;
nibble b = 0b1100;
char c = foo(a,b);
print_bits(c); 
// output: 0b11010010

Note: 4 bits is just for illustration, I want to do this with two 32bit ints. 注意:4位仅用于说明,我想用两个32位整数执行此操作。

This is called the perfect shuffle operation, and it's discussed at length in the Bible Of Bit Bashing, Hacker's Delight by Henry Warren, section 7-2 "Shuffling Bits." 这被称为完美的随机操作,它在“抨击圣经”,“亨利·沃伦的黑客喜悦 ”,第7-2节“洗牌比特”中详细讨论过。

Assuming x is a 32-bit integer with a in its high-order 16 bits and b in its low-order 16 bits: 假设x与32位整数a在其高位16位和b在其低位16位:

   unsigned int x = (a << 16) | b;   /* put a and b in place */

the following straightforward C-like code accomplishes the perfect shuffle: 以下直截了当的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;

He also gives an alternative form which is faster on some CPUs, and (I think) a little more clear and extensible: 他还给出了一种替代形式,它在一些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);

I see you have edited your question to ask for a 64-bit result from two 32-bit inputs. 我看到你编辑了你的问题,要求两个32位输入的64位结果。 I'd have to think about how to extend Warren's technique. 我不得不考虑如何扩展沃伦的技术。 I think it wouldn't be too hard, but I'd have to give it some thought. 我认为这不会太难,但我必须考虑一下。 If someone else wanted to start here and give a 64-bit version, I'd be happy to upvote them. 如果有人想从这里开始并提供64位版本,我很乐意对它们进行投票。

EDITED FOR 64 BITS 编辑64位

I extended the second solution to 64 bits in a straightforward way. 我以简单的方式将第二个解决方案扩展到64位。 First I doubled the length of each of the constants. 首先,我将每个常量的长度加倍。 Then I added a line at the beginning to swap adjacent double-bytes and intermix them. 然后我在开头添加了一行来交换相邻的双字节并混合它们。 In the following 4 lines, which are pretty much the same as the 32-bit version, the first line swaps adjacent bytes and intermixes, the second line drops down to nibbles, the third line to double-bits, and the last line to single bits. 在以下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);

From Stanford "Bit Twiddling Hacks" page: https://graphics.stanford.edu/~seander/bithacks.html#InterleaveTableObvious 来自斯坦福大学的“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);
 }

Look at the page they propose different and faster algorithms to achieve the same. 看看他们提出的不同的页面和更快的算法来实现相同的目标。

Here is a loop-based solution that is hopefully more readable than some of the others already here. 这是一个基于循环的解决方案,希望比这里已有的其他解决方案更具可读性。

#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);
}

Like so: 像这样:

#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;
}

I have used the 2 tricks/operations used in this post How do you set, clear, and toggle a single bit? 我已经使用了本文中使用的2个技巧/操作如何设置,清除和切换一个位? of setting a bit at particular index and checking the bit at particular index . setting a bit at particular indexchecking the bit at particular index

The following code is implemented using these 2 operations only. 以下代码仅使用这两个操作实现。

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);

Output: 210 which is 0b11010010 输出: 210 ,即0b11010010

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM