簡體   English   中英

C 在無符號整數中反轉位

[英]C reverse bits in unsigned integer

我正在使用按位運算符將無符號整數轉換為二進制,並且當前執行 integer & 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,這是不正確的......所以我想在運行循環之前顛倒最初的位順序,但我不確定如何做到這一點?

反轉一個字中的位很煩人,只是以相反的順序輸出它們更容易。 例如,

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

您可以改為從左向右移動,即將一個從 MSB 移到 LSB,例如:

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

你可以從大端到小端循環遍歷這些位。

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

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

您可以反轉一個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; 
}

請記住包含數學庫。 快樂編碼:)

我相信問題是問如何以相反的順序輸出。

有趣的答案(遞歸):

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

反轉整數位的最佳方法是:

  1. 這是非常有效的。
  2. 它僅在最左邊的位為 1 時運行。

代碼片段

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

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 的回答)。

顛倒整數值的位順序然后從低端挑選位似乎很愚蠢,而保持不變並從高端挑選位是微不足道的。

您想將整數轉換為文本表示,在本例中為 base-2(二進制)。 計算機一直將整數轉換為文本,最常見的是基數為 10 或基數為 16。

一個簡單的內置解決方案是:

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

但這不是 C 中的標准。(請參閱是否有 printf 轉換器以二進制格式打印?

數字首先用最高有效位(或位)寫入,因此重復取最低有效位(或位)是工作的一半 您必須收集數字並以相反的順序組合或輸出它們。

要將值 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”。

您可以像輸出它們一樣反轉位,而是將它們存儲在另一個整數中,然后再次執行:

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完成

我想出了一個解決方案,它不涉及任何按位運算符的應用。 它在空間和時間上都是低效的。

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;

這是一個整數中反向位的 golang 版本,如果有人正在尋找的話。 我用類似於 c 中的 string reverse 的方法寫了這個。 從位 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)
    }
}`

這是我的位移版本,我認為它非常簡潔。 但不適用於前導零。 主要思想如下

  • 輸入在變量 a 中,最終答案在 b 中

  • 繼續從 using (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); }

下面使用存儲每個字節的所有反轉值的table[byte] == reversed_bytetable[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]);
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM