C无符号整数的反转位

时间:2012-02-04 21:42:15

标签: c binary int bit-manipulation unsigned

我正在使用按位运算符将无符号整数转换为二进制,并且当前执行整数& 1检查位是1还是0并输出,然后右移1除以2.但是这些位以错误的顺序返回(反向),所以我想在开始之前反转整数中的位顺序。

有一种简单的方法吗?

实施例: 所以如果我给了unsigned int 10 = 1010

while (x not eq 0) 
  if (x & 1)
    output a '1'
  else 
    output a '0'
  right shift x by 1

这会返回0101,这是不正确的...所以我想在运行循环之前反转最初的位顺序,但我不确定该怎么做?

14 个答案:

答案 0 :(得分:20)

反转单词中的位很烦人,只是以相反的顺序输出它们更容易。如,

void write_u32(uint32_t x)
{
    int i;
    for (i = 0; i < 32; ++i)
        putchar((x & ((uint32_t) 1 << (31 - i)) ? '1' : '0');
}

这是反转位顺序的典型解决方案:

uint32_t reverse(uint32_t x)
{
    x = ((x >> 1) & 0x55555555u) | ((x & 0x55555555u) << 1);
    x = ((x >> 2) & 0x33333333u) | ((x & 0x33333333u) << 2);
    x = ((x >> 4) & 0x0f0f0f0fu) | ((x & 0x0f0f0f0fu) << 4);
    x = ((x >> 8) & 0x00ff00ffu) | ((x & 0x00ff00ffu) << 8);
    x = ((x >> 16) & 0xffffu) | ((x & 0xffffu) << 16);
    return x;
}

答案 1 :(得分:2)

你可以从左向右移动,即将一个从MSB移到LSB,例如:

unsigned n = 20543;
unsigned x = 1<<31;
while (x) {
    printf("%u ", (x&n)!=0);
    x = x>>1;
}

答案 2 :(得分:2)

你可以循环从大端到小端的位。

#define N_BITS (sizeof(unsigned) * CHAR_BIT)
#define HI_BIT (1 << (N_BITS - 1))

for (int i = 0; i < N_BITS; i++) {
     printf("%d", !!(x & HI_BIT));
     x <<= 1;
}

其中!!也可以写为!=0>> (N_BITS - 1)

答案 3 :(得分:1)

您可以像输出它们一样反转这些位,而是将它们存储在另一个整数中,然后再次执行:

for (i = 0; i < (sizeof(unsigned int) * CHAR_BIT); i++)
{
  new_int |= (original_int & 1);
  original_int = original_int >> 1;
  new_int = new_int << 1;
}

或者你可以做相反的事情,转移你的面具:

unsigned int mask = 1 << ((sizeof(unsigned int) * CHAR_BIT) - 1);
while (mask > 0)
{
  bit = original_int & mask;
  mask = mask >> 1;
  printf("%d", (bit > 0));
}

如果您想删除前导0,您可以等待1打印,或者进行初步操作:

unsigned int mask = 1 << ((sizeof(unsigned int) * CHAR_BIT) - 1);
while ((mask > 0) && ((original_int & mask) == 0))
  mask = mask >> 1;
do
{
  bit = original_int & mask;
  mask = mask >> 1;
  printf("%d", (bit > 0));
} while (mask > 0);

这样你就可以将面具放在第一个要打印的1上,而忘记前导0的

但请记住:打印整数的二进制值只需使用printf

即可

答案 4 :(得分:1)

unsigned int rev_bits(unsigned int input)
{
    unsigned int output = 0;
    unsigned int n = sizeof(input) << 3;
    unsigned int i = 0;

    for (i = 0; i < n; i++)
        if ((input >> i) & 0x1)
            output |=  (0x1 << (n - 1 - i));

    return output;
}

答案 5 :(得分:1)

反转整数位的最佳方法是:

  1. 这非常有效。
  2. 仅在最左边的位为1时运行。

CODE SNIPPET

int reverse ( unsigned int n )
{
    int x = 0;
    int mask = 1;
    while ( n > 0 )
    {
        x = x << 1;
        if ( mask & n )
            x = x | 1;
        n = n >> 1;
    }
    return x;
}

答案 6 :(得分:1)

颠倒一个整数值的位顺序然后从低端提取位似乎很愚蠢,而保持不变并从高端提取位是微不足道的。

您想将整数转换为 文本 表示,在本例中为 base-2(二进制)。计算机一直将整数转换为文本,最常见的是基数为 10 或基数为 16。

一个简单的内置解决方案是:

printf('%b', 123);  // outputs 1111011

但这不是 C 中的标准。(参见 Is there a printf converter to print in binary format?

数字首先用最高有效位(或位)书写,因此重复取最低有效位(或位)是一半的工作。您必须收集数字并按相反顺序组合或输出它们。

要将值 123 显示为 base-10,您应该:

  • 将 123 除以 10,得到 12 余数 3。
  • 将 12 除以 10,得到 1 余数 2。
  • 最后,将 1 除以 10,得到 0 余数 1。(0 是停止点。)
  • 以相反的顺序显示余数 (3, 2, 1),以显示“123”。

我们可以在 123 之前放置任意数量的零,但这不合适,因为它们没有任何贡献。更大的数字需要更长的字符串(“123”、“123000”、“123000000”)。使用这种方法,在计算最重要的数字之前,您不知道需要多少位数字,因此在计算完所有数字之前,您无法输出第一个数字。

或者,您可以先计算最高有效位。它看起来有点复杂。特别是在 base-2 以外的碱基中。再次以 123 开头:

  • 将 123 除以 1000,得到 0 余数 123。
  • 将 123 除以 100,得到 1 个余数 23。
  • 将 23 除以 10,得到 2 余数 3。
  • 最后,将 3 除以 1,得到 3 个余数 0。(0 是停止点。)
  • 相同的顺序显示(0、1、2、3),跳过前导零,显示“123”。

您可以在计算时按顺序输出数字。你必须从一个足够大的除数开始。对于 uint16,它是 10000;对于 uint32,它是 1000000000。

要将值 123 显示为 base-2,请使用第一种方法:

  • 将 123 除以 2,得到 61 个余数 1。
  • 将 61 除以 2,得到 30 余 1。
  • 将 30 除以 2,得到 15 个余数 0。
  • 15 除以 2,得到 7 余数 1。
  • 7 除以 2,得到 3 余数 1。
  • 将 3 除以 2,得到 1 余数 1。
  • 最后,将 1 除以 2,得到 0 余数 1。(0 是停止点。)
  • 以相反的顺序显示余数 (1,1,0,1,1,1,1),以显示“1111011”。

(除以 2 可以通过右移 1 位来完成。)

第二种方法按顺序生成数字(位)。

  • 将 123 除以 256,得到 0 余数 123。
  • 将 123 除以 128,得到 0 余数 123。
  • 将 123 除以 64,得到 1 个余数 59。
  • 将 59 除以 32,得到 1 个余数 27。
  • 将 27 除以 16,得到 1 个余数 11。
  • 将 11 除以 8,得到 1 余数 3。
  • 将 3 除以 4,得到 0 余数 3。
  • 将 2 除以 2,得到 1 个余数 1。
  • 最后,将 1 除以 1,得到 1 个余数 0。(0 是停止点。)
  • 相同的顺序显示 (0,0,1,1,1,1,0,1,1),跳过任何前导零,显示“1111011”。

(这些除法可以使用比较来完成。比较值可以通过除以 2 生成,这意味着右移 1 位。)

这些解决方案中的任何一个都可能需要修改以防止值 0 显示为空(也称为“”或空字符串)而不是“0”。

答案 7 :(得分:0)

我提出了一个解决方案,它不涉及任何按位运算符的应用。它在空间和时间方面都是低效的。

int arr[32];
for(int i=0;i<32;i++)
{
    arr[i]=A%2;
    A=A/2;
}
double res=1;
double re=0;
for(int i=0;i<32;i++)
{
    int j=31-i;
    res=arr[i];
    while(j>0)
    {
        res=res*2;
        j--;
    }
    re=re+res;
}
cout<<(unsigned int )re;

答案 8 :(得分:0)

这是一个整数反转位的golang版本,如果有人正在寻找一个。我在c中用类似于字符串反向的方法编写了这个。从位0到15(31/2),将位i与位(31-i)交换。请检查以下代码。

package main
import "fmt"

func main() {
    var num = 2
    //swap bits at index i and 31-i for i between 0-15
    for i := 0; i < 31/2; i++ {
        swap(&num, uint(i))
    }
    fmt.Printf("num is %d", num)
}

//check if bit at index is set
func isSet(num *int, index uint) int {
    return *num & (1 << index)
}

//set bit at index
func set(num *int, index uint) {
    *num = *num | (1 << index)
}

//reset bit at index
func reSet(num *int, index uint) {
    *num = *num & ^(1 << index)
}

//swap bits on index and 31-index
func swap(num *int, index uint) {
    //check index and 31-index bits
    a := isSet(num, index)
    b := isSet(num, uint(31)-index)
    if a != 0 {
        //bit at index is 1, set 31-index
        set(num, uint(31)-index)
    } else {
        //bit at index is 0, reset 31-index
        reSet(num, uint(31)-index)
    }
    if b != 0 {
        set(num, index)
    } else {
        reSet(num, index)
    }
}`

答案 9 :(得分:0)

您可以撤消unsigned 32-bit integer并使用以下reverse函数返回:

unsigned int reverse(unsigned int A) {
    unsigned int B = 0;
    for(int i=0;i<32;i++){
        unsigned int j = pow(2,31-i);
        if((A & (1<<i)) == (1<<i)) B += j; 
    }
return B; 
}

请记住包含数学库。快乐的编码:)

答案 10 :(得分:0)

我认为问题是如何以相反的顺序输出。

有趣的回答(递归):

#include <stdio.h>

void print_bits_r(unsigned int x){
    if(x==0){
       printf("0");
       return;
    }
    unsigned int n=x>>1;
    if(n!=0){
       print_bits_r(n);
    }
    if(x&1){
        printf("1");
    }else{
        printf("0");
    }
}


void print_bits(unsigned int x){
    printf("%u=",x);
    print_bits_r(x);
    printf("\n");
}

int main(void) {
    print_bits(10u);//1010
    print_bits((1<<5)+(1<<4)+1);//110001
    print_bits(498598u);//1111001101110100110
    return 0;
}

预期产出:

10=1010
49=110001
498598=1111001101110100110

顺序版本(首先选择高位):

#include <limits.h>//Defines CHAR_BIT
//....
void print_bits_r(unsigned int x){
    //unsigned int mask=(UINT_MAX>>1)+1u;//Also works...
    unsigned int mask=1u<<(CHAR_BIT*sizeof(unsigned int)-1u);
    int start=0;
    while(mask!=0){
        if((x&mask)!=0){
            printf("1");
            start=1;
        }else{
            if(start){
                printf("0");
            }
        }
        mask>>=1;
    }
    if(!start){
       printf("0");
    }    
}

答案 11 :(得分:0)

Dietrich Epp提出的第二个答案可能是在具有高速缓存的现代处理器上最好的选择。但是,在典型的微控制器上并非如此,以下情况不仅更快,而且更通用,更紧凑(在C语言中):

// reverse a byte
uint8_t reverse_u8(uint8_t x)
{
   const unsigned char * rev = "\x0\x8\x4\xC\x2\xA\x6\xE\x1\x9\x5\xD\x3\xB\x7\xF";
   return rev[(x & 0xF0) >> 4] | (rev[x & 0x0F] << 4);
}

// reverse a word
uint16_t reverse_u16(uint16_t x)
{
   return reverse_u8(x >> 8) | (reverse_u8(x & 0xFF) << 8);
}

// reverse a long
uint32_t reverse_u32(uint32_t x)
{
   return reverse_u16(x >> 16) | (reverse_u16(x & 0xFFFF) << 16);
}

该代码可以轻松地转换为Java,Go,Rust等。当然,如果只需要打印数字,则最好简单地以相反的顺序打印(请参见Dietrich Epp的回答)。

答案 12 :(得分:0)

这是我的翻译版本,我认为它非常简洁。但是不适用于前导零。主要思想如下

  • 输入位于变量a中,最终答案位于b

  • 继续从(a&1)使用中提取最右边的位

  • 对b进行左移b或将其移至下一位

  • 将a右移到下一位

     #include <stdio.h>
    
     void main()
     {
       int a = 23;
       int b = 0;
       while(a!=0)
       {
         b = (b<<1)|(a&1);
         a = a>>1;
       }
       printf("reversed bits gives %d\n", b);
      }
    

答案 13 :(得分:0)

下面使用一个表,该表存储了每个字节的所有反转值,table[byte] == reversed_byte,并反转了无符号整数的 4 个字节。计算速度比其他答案快。

#include <stdint.h>

uint32_t reverse_bits(uint32_t n) {
static const uint8_t table[256] =
    {
      0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0,
      0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8,
      0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4,
      0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC,
      0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2,
      0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA,
      0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6,
      0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE,
      0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1,
      0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9,
      0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5,
      0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD,
      0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3,
      0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB,
      0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7,
      0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF
    };

// 1 2 3 4 -> byte 4 becomes 1, 3 becomes 2, 2 becomes 3 and 1 becomes 4.
return (table[n & 0xff] << 24) | (table[(n >> 8) & 0xff] << 16) |
        (table[(n >> 16) & 0xff] << 8) | (table[(n >> 24) & 0xff]);
}