Di C / C ++ apa cara termudah untuk membalik urutan bit dalam satu byte?

110

Meskipun ada banyak cara untuk membalik urutan bit dalam satu byte, saya ingin tahu apa yang "paling sederhana" untuk diterapkan oleh pengembang. Dan dengan membalik maksud saya:

1110 -> 0111
0010 -> 0100

Ini mirip dengan, tetapi bukan duplikat dari pertanyaan PHP ini .

Ini mirip dengan, tetapi bukan duplikat dari pertanyaan C ini . Pertanyaan ini menanyakan metode termudah untuk diterapkan oleh pengembang. "Algoritma Terbaik" berkaitan dengan memori dan kinerja cpu.

nathan
sumber
Gunakan perakitan inline. Lebih baik, letakkan fungsi tersebut ke dalam unit terjemahan terpisah. Memiliki satu modul bahasa assembly untuk setiap platform target. Biarkan proses build memilih modul.
Thomas Matthews
@Andreas Implementasi yang paling sederhana
nathan

Jawaban:

102

Jika Anda berbicara tentang satu byte, pencarian tabel mungkin adalah pilihan terbaik, kecuali karena alasan tertentu Anda tidak memiliki 256 byte yang tersedia.

e. James
sumber
12
Jika kita berbicara tentang sesuatu yang mudah diterapkan tanpa menyalin solusi yang sudah jadi, membuat tabel pencarian masih membutuhkan solusi lain. (Tentu saja seseorang mungkin melakukannya dengan tangan, tapi itu rawan kesalahan dan memakan waktu ...)
Arkku
7
Anda dapat memeras array menjadi kurang dari 256 byte jika Anda mengabaikan palindrom.
wilhelmtell
8
@wilhelmtell - Anda memerlukan tabel untuk mengetahui palindrome mana yang dimaksud.
Mark Ransom
6
@wilhelmtell: Nah, untuk menulis skrip, kita masih membutuhkan solusi lain, itulah maksud saya - tabel pencarian mudah digunakan tetapi tidak mudah dibuat. (Kecuali dengan menyalin tabel pencarian yang sudah jadi, tetapi mungkin juga menyalin solusi apa pun.) Misalnya, jika solusi "paling sederhana" dianggap sebagai solusi yang dapat ditulis di atas kertas dalam ujian atau wawancara, saya tidak akan mulai membuat tabel pemeta dengan tangan dan membuat program untuk melakukannya akan menyertakan solusi yang berbeda (yang akan lebih sederhana daripada yang menyertakan keduanya dan tabel).
Arkku
4
@Arkku yang saya maksud adalah menulis skrip yang menampilkan tabel 256 byte pertama dan pemetaan terbalik mereka. Ya, Anda kembali menulis fungsi kebalikannya, tetapi sekarang dalam bahasa skrip favorit Anda, dan itu bisa seburuk yang Anda inginkan - Anda akan membuangnya segera setelah selesai dan Anda menjalankannya sekali. Miliki keluaran skrip sebagai kode C, bahkan:unsigned int rtable[] = {0x800, 0x4000, ...}; . Kemudian buang skrip dan lupakan Anda pernah memilikinya. Ini jauh lebih cepat untuk menulis daripada kode C ++ yang setara, dan itu hanya akan berjalan sekali, jadi Anda mendapatkan runtime O (1) dalam kode C ++ Anda.
wilhelmtell
227

Ini harus bekerja:

unsigned char reverse(unsigned char b) {
   b = (b & 0xF0) >> 4 | (b & 0x0F) << 4;
   b = (b & 0xCC) >> 2 | (b & 0x33) << 2;
   b = (b & 0xAA) >> 1 | (b & 0x55) << 1;
   return b;
}

Pertama empat bit kiri ditukar dengan empat bit kanan. Kemudian semua pasangan yang berdekatan ditukar dan kemudian semua bit tunggal yang berdekatan. Ini menghasilkan urutan terbalik.

sth
sumber
26
Cukup pendek dan cepat, tetapi tidak sederhana.
Mark Ransom
3
Pendekatan ini juga secara tepat menggeneralisasi untuk melakukan pertukaran byte untuk ketangguhan.
Boojum
2
Bukan pendekatan yang paling sederhana, tapi saya suka +1.
nathan
7
Ya, itu sederhana. Ini semacam algoritma bagi dan taklukkan. Luar biasa!
kiewic
Apakah lebih cepat daripada metode yang disarankan oleh @Arkku di bawah ini?
qed
123

Saya pikir tabel pencarian harus menjadi salah satu metode paling sederhana. Namun, Anda tidak memerlukan tabel pencarian lengkap.

//Index 1==0b0001 => 0b1000
//Index 7==0b0111 => 0b1110
//etc
static unsigned char lookup[16] = {
0x0, 0x8, 0x4, 0xc, 0x2, 0xa, 0x6, 0xe,
0x1, 0x9, 0x5, 0xd, 0x3, 0xb, 0x7, 0xf, };

uint8_t reverse(uint8_t n) {
   // Reverse the top and bottom nibble then swap them.
   return (lookup[n&0b1111] << 4) | lookup[n>>4];
}

// Detailed breakdown of the math
//  + lookup reverse of bottom nibble
//  |       + grab bottom nibble
//  |       |        + move bottom result into top nibble
//  |       |        |     + combine the bottom and top results 
//  |       |        |     | + lookup reverse of top nibble
//  |       |        |     | |       + grab top nibble
//  V       V        V     V V       V
// (lookup[n&0b1111] << 4) | lookup[n>>4]

Ini cukup sederhana untuk membuat kode dan memverifikasi secara visual.
Pada akhirnya ini bahkan mungkin lebih cepat daripada tabel penuh. Bit arith murah dan tabelnya mudah dipasang pada baris cache.

deft_code
sumber
10
Itu adalah cara terbaik untuk mengurangi kompleksitas solusi tabel. +1
e. James
3
Bagus, tapi akan memberi Anda cache miss.
Johan Kotlinski
7
@ kotlinski: apa yang menyebabkan cache tidak ditemukan? Saya pikir versi tabel kecil mungkin lebih efisien dalam cache daripada yang besar. Pada Core2 saya, garis cache berukuran 64 byte, tabel penuh akan menjangkau beberapa baris, sedangkan tabel yang lebih kecil dengan mudah menyesuaikan satu baris tunggal.
deft_code
4
@kotlinski: Lokalitas temporal lebih penting untuk cache ditemukan atau strategi penggantian, daripada lokalitas alamat
cfi
6
@Harshdeep: Pertimbangkan indeks yang dikodekan biner dari entri tabel. indeks b0000 (0) -> b0000 (0x0) membosankan; b0001(1) -> b1000(0x8), b0010(2) -> b0100(0x4), b1010(10) -> b0101(0x5). Lihat polanya? Ini cukup sederhana sehingga Anda dapat menghitungnya di kepala Anda (jika Anda dapat membaca biner, jika tidak Anda akan memerlukan kertas untuk menyelesaikannya). Adapun lompatan bahwa membalik bilangan bulat 8 bit sama dengan membalik bagian 4 bit kemudian menukarnya; Saya mengklaim pengalaman dan intuisi (atau sihir).
deft_code
46

Lihat peretasan yang sedikit memutar-mutar untuk banyak solusi. Copypasting dari sana jelas mudah diterapkan. =)

Misalnya (pada CPU 32-bit):

uint8_t b = byte_to_reverse;
b = ((b * 0x0802LU & 0x22110LU) | (b * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16;

Jika dengan "mudah diimplementasikan" berarti sesuatu yang dapat dilakukan tanpa referensi dalam ujian atau wawancara kerja, maka taruhan teraman mungkin adalah menyalin bit yang tidak efisien satu per satu ke variabel lain dalam urutan terbalik (sudah ditunjukkan di jawaban lain) ).

Arkku
sumber
1
Dari URL Anda: CPU 32 bit: b = ((b * 0x0802LU & 0x22110LU) | (b * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16;
Joshua
1
@ Joshua: Itu juga favorit pribadi saya. Peringatan (seperti yang dinyatakan di halaman tertaut) adalah bahwa itu perlu ditetapkan atau dibuang ke dalam uint8_t atau akan ada sampah di bit atas.
Arkku
42

Karena tidak ada yang memposting solusi pencarian tabel lengkap, inilah solusi saya:

unsigned char reverse_byte(unsigned char x)
{
    static const unsigned char table[] = {
        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,
    };
    return table[x];
}
fredoverflow
sumber
2
Berguna, terima kasih. Sepertinya metode perpindahan yang lebih lambat saya membatasi kinerja dalam aplikasi yang disematkan. Menempatkan tabel dalam ROM pada PIC (dengan tambahan kata kunci rom).
flend
1
Metode yang lebih sederhana: graphics.stanford.edu/~seander/bithacks.html#BitReverseTable
sleepsort
25
template <typename T>
T reverse(T n, size_t b = sizeof(T) * CHAR_BIT)
{
    assert(b <= std::numeric_limits<T>::digits);

    T rv = 0;

    for (size_t i = 0; i < b; ++i, n >>= 1) {
        rv = (rv << 1) | (n & 0x01);
    }

    return rv;
}

EDIT:

Mengonversinya menjadi template dengan bitcount opsional

dan dan
sumber
@nvl - diperbaiki. Saya mulai membuatnya sebagai template tetapi memutuskan di tengah jalan untuk tidak melakukannya ... terlalu banyak & gt & lt
anddan
Untuk pedenatry ekstra, ganti sizeof(T)*8dengan sizeof(T)*CHAR_BITS.
Pillsy
6
@andand Untuk liontin ekstra, ganti sizeof(T)*CHAR_BITdengan std::numeric_limits<T>::digits(hampir 4 tahun kelayakan nanti).
Morwenn
1
Seharusnya CHAR_BITtidak CHAR_BITS.
Xunie
1
itu harus rv = (rv << 1) | (n & 0x01);
Vignesh
16

Dua baris:

for(i=0;i<8;i++)
     reversed |= ((original>>i) & 0b1)<<(7-i);

atau jika Anda memiliki masalah dengan bagian "0b1":

for(i=0;i<8;i++)
     reversed |= ((original>>i) & 1)<<(7-i);

"original" adalah byte yang ingin Anda balikkan. Hasilnya "dibalik", diinisialisasi ke 0.

Daniel
sumber
14

Meskipun mungkin tidak portabel, saya akan menggunakan bahasa assembly.
Banyak bahasa assembly memiliki instruksi untuk memutar sedikit ke dalam bendera carry dan memutar bendera carry ke dalam kata (atau byte).

Algoritmanya adalah:

for each bit in the data type:
  rotate bit into carry flag
  rotate carry flag into destination.
end-for

Kode bahasa tingkat tinggi untuk ini jauh lebih rumit, karena C dan C ++ tidak mendukung rotating to carry dan rotating from carry. Bendera pembawa harus dimodelkan.

Edit: Bahasa assembly misalnya

;  Enter with value to reverse in R0.
;  Assume 8 bits per byte and byte is the native processor type.
   LODI, R2  8       ; Set up the bit counter
Loop:
   RRC, R0           ; Rotate R0 right into the carry bit.
   RLC, R1           ; Rotate R1 left, then append carry bit.
   DJNZ, R2  Loop    ; Decrement R2 and jump if non-zero to "loop"
   LODR, R0  R1      ; Move result into R0.
Thomas Matthews
sumber
7
Saya pikir jawaban ini adalah kebalikan dari sederhana. Non-portabel, rakitan, dan cukup kompleks untuk ditulis dalam pseudo-code alih-alih rakitan sebenarnya.
deft_code
3
Ini cukup sederhana. Saya memasukkannya ke dalam pseudo-code karena mnemonik perakitan khusus untuk jenis prosesor dan ada banyak keturunan di luar sana. Jika Anda mau, saya dapat mengedit ini untuk menunjukkan bahasa assembly sederhana.
Thomas Matthews
Seseorang dapat melihat apakah pengoptimalan kompiler disederhanakan menjadi instruksi perakitan yang sesuai.
Sparky
12

Saya menemukan solusi berikut lebih sederhana daripada algoritma mengutak-atik bit lain yang pernah saya lihat di sini.

unsigned char reverse_byte(char a)
{

  return ((a & 0x1)  << 7) | ((a & 0x2)  << 5) |
         ((a & 0x4)  << 3) | ((a & 0x8)  << 1) |
         ((a & 0x10) >> 1) | ((a & 0x20) >> 3) |
         ((a & 0x40) >> 5) | ((a & 0x80) >> 7);
}

Itu mendapatkan setiap bit dalam byte, dan menggesernya sesuai, mulai dari yang pertama hingga yang terakhir.

Penjelasan:

   ((a & 0x1) << 7) //get first bit on the right and shift it into the first left position 
 | ((a & 0x2) << 5) //add it to the second bit and shift it into the second left position
  //and so on
dau_sama
sumber
Cantik! Favorit saya sejauh ini.
Nick Rameau
Ini tentu saja sederhana, tetapi harus ditunjukkan bahwa waktu eksekusi adalah O (n) daripada O (log₂ n), di mana n adalah jumlah bit (8, 16, 32, 64, dll.).
Todd Lehman
10

Cara termudah mungkin adalah dengan mengulang posisi bit dalam satu lingkaran:

unsigned char reverse(unsigned char c) {
   int shift;
   unsigned char result = 0;
   for (shift = 0; shift < CHAR_BIT; shift++) {
      if (c & (0x01 << shift))
         result |= (0x80 >> shift);
   }
   return result;
}
sth
sumber
CHAR_BIT, tanpa 's'
ljrk
Mengapa digunakan CHAR_BITjika Anda menganggap charmemiliki 8 bit?
chqrlie
6

Anda mungkin tertarik pada std::vector<bool>(yang sangat padat) danstd::bitset

Ini harus yang paling sederhana seperti yang diminta.

#include <iostream>
#include <bitset>
using namespace std;
int main() {
  bitset<8> bs = 5;
  bitset<8> rev;
  for(int ii=0; ii!= bs.size(); ++ii)
    rev[bs.size()-ii-1] = bs[ii];
  cerr << bs << " " << rev << endl;
}

Pilihan lain mungkin lebih cepat.

EDIT: Saya berhutang solusi menggunakan std::vector<bool>

#include <algorithm>
#include <iterator>
#include <iostream>
#include <vector>
using namespace std;
int main() {
  vector<bool> b{0,0,0,0,0,1,0,1};
  reverse(b.begin(), b.end());
  copy(b.begin(), b.end(), ostream_iterator<int>(cerr));
  cerr << endl;
}

Contoh kedua membutuhkan ekstensi c ++ 0x (untuk menginisialisasi array dengan {...}). Keuntungan menggunakan a bitsetatau a std::vector<bool>(atau a boost::dynamic_bitset) adalah Anda tidak terbatas pada byte atau kata tetapi dapat membalikkan jumlah bit yang berubah-ubah.

HTH

baol
sumber
Bagaimana bitset lebih sederhana daripada pod di sini? Tunjukkan kodenya, atau tidak.
wilhelmtell
Sebenarnya, saya pikir kode itu akan membalikkan bitset, dan kemudian mengembalikannya ke aslinya. Ubah ii! = Size (); ke ii <size () / 2; dan itu akan melakukan pekerjaan yang lebih baik =)
Viktor Sehr
(@ viktor-sehr no, tidak akan, rev berbeda dengan bs). Bagaimanapun, saya sendiri tidak menyukai jawabannya: Saya pikir ini adalah kasus di mana aritmatika biner dan operator shift lebih cocok. Itu masih tetap yang paling sederhana untuk dipahami.
baol
Bagaimana jika std::vector<bool> b = { ... }; std::vector<bool> rb ( b.rbegin(), b.rend()); - menggunakan iterator terbalik secara langsung?
MSalters
@ MSalters Saya suka kekekalannya.
baol
6

Untuk kasus yang sangat terbatas dari input 8-bit yang konstan, metode ini tidak memerlukan biaya memori atau CPU pada saat run-time:

#define MSB2LSB(b) (((b)&1?128:0)|((b)&2?64:0)|((b)&4?32:0)|((b)&8?16:0)|((b)&16?8:0)|((b)&32?4:0)|((b)&64?2:0)|((b)&128?1:0))

Saya menggunakan ini untuk ARINC-429 di mana urutan bit (endianness) label berlawanan dengan kata lainnya. Label seringkali konstan, dan biasanya dalam oktal.

Inilah cara saya menggunakannya untuk mendefinisikan sebuah konstanta, karena spesifikasi mendefinisikan label ini sebagai oktal 205 big-endian.

#define LABEL_HF_COMM MSB2LSB(0205)

Contoh lainnya:

assert(0b00000000 == MSB2LSB(0b00000000));
assert(0b10000000 == MSB2LSB(0b00000001));
assert(0b11000000 == MSB2LSB(0b00000011));
assert(0b11100000 == MSB2LSB(0b00000111));
assert(0b11110000 == MSB2LSB(0b00001111));
assert(0b11111000 == MSB2LSB(0b00011111));
assert(0b11111100 == MSB2LSB(0b00111111));
assert(0b11111110 == MSB2LSB(0b01111111));
assert(0b11111111 == MSB2LSB(0b11111111));
assert(0b10101010 == MSB2LSB(0b01010101));
Bob Stein
sumber
5

Ada banyak cara untuk membalikkan bit tergantung pada apa yang Anda maksud dengan "cara termudah".


Terbalik dengan Rotasi

Mungkin yang paling logis, terdiri dari memutar byte sambil menerapkan mask pada bit pertama (n & 1):

unsigned char reverse_bits(unsigned char b)
{
    unsigned char   r = 0;
    unsigned        byte_len = 8;

    while (byte_len--) {
        r = (r << 1) | (b & 1);
        b >>= 1;
    }
    return r;
}

1) Karena panjang unsigner char adalah 1 byte, yang sama dengan 8 bit, berarti kita akan memindai setiap bit while (byte_len--)

2) Pertama-tama kami memeriksa apakah b sedikit di ekstrem kanan dengan (b & 1); jika demikian kita set bit 1 pada r dengan| dan pindahkan hanya 1 bit ke kiri dengan mengalikan r dengan 2 dengan(r << 1)

3) Kemudian kita membagi unsigned char b dengan 2 dengan b >>=1untuk menghapus bit yang terletak di paling kanan dari variabel b. Sebagai pengingat, b >> = 1; setara dengan b / = 2;


Terbalik dalam Satu Baris

Solusi ini dikaitkan dengan Rich Schroeppel di bagian Programming Hacks

unsigned char reverse_bits3(unsigned char b)
{
    return (b * 0x0202020202ULL & 0x010884422010ULL) % 0x3ff;
}

1) Operasi penggandaan (b * 0x0202020202ULL) membuat lima salinan terpisah dari pola byte 8-bit untuk menyebar ke nilai 64-bit.

2) Operasi AND (& 0x010884422010ULL) memilih bit yang berada pada posisi yang benar (terbalik), relatif terhadap setiap grup bit 10-bit.

3) Bersama-sama operasi perkalian dan operasi AND menyalin bit dari byte asli sehingga masing-masing hanya muncul di salah satu dari set 10-bit. Posisi bit yang dibalik dari byte asli bertepatan dengan posisi relatifnya dalam set 10-bit.

4) Langkah terakhir (% 0x3ff), yang melibatkan pembagian modulus oleh 2 ^ 10 - 1 memiliki efek penggabungan bersama setiap set 10 bit (dari posisi 0-9, 10-19, 20-29, ...) dalam nilai 64-bit. Mereka tidak tumpang tindih, jadi langkah penjumlahan yang mendasari pembagian modulus berperilaku seperti operasi OR.


Bagi dan Taklukkan Solusi

unsigned char reverse(unsigned char b) {
   b = (b & 0xF0) >> 4 | (b & 0x0F) << 4;
   b = (b & 0xCC) >> 2 | (b & 0x33) << 2;
   b = (b & 0xAA) >> 1 | (b & 0x55) << 1;
   return b;
}

Ini adalah jawaban yang paling banyak dipilih dan meskipun ada beberapa penjelasan, menurut saya bagi kebanyakan orang terasa sulit untuk memvisualisasikan arti sebenarnya dari 0xF0, 0xCC, 0xAA, 0x0F, 0x33 dan 0x55.

Itu tidak memanfaatkan '0b' yang merupakan ekstensi GCC dan disertakan sejak standar C ++ 14, dirilis pada Desember 2014, jadi beberapa saat setelah jawaban ini berasal dari April 2010

Konstanta bilangan bulat dapat dituliskan sebagai konstanta biner, yang terdiri dari deret angka '0' dan '1', diawali dengan '0b' atau '0B'. Ini sangat berguna di lingkungan yang banyak beroperasi pada level bit (seperti mikrokontroler).

Silakan periksa cuplikan kode di bawah ini untuk mengingat dan memahami lebih baik solusi ini di mana kami bergerak setengah demi setengah:

unsigned char reverse(unsigned char b) {
   b = (b & 0b11110000) >> 4 | (b & 0b00001111) << 4;
   b = (b & 0b11001100) >> 2 | (b & 0b00110011) << 2;
   b = (b & 0b10101010) >> 1 | (b & 0b01010101) << 1;
   return b;
}

NB: Itu >> 4 karena ada 8 bit dalam 1 byte, yang merupakan karakter unsigned jadi kami ingin mengambil setengah lainnya, dan seterusnya.

Kami dapat dengan mudah menerapkan solusi ini ke 4 byte dengan hanya dua baris tambahan dan mengikuti logika yang sama. Karena kedua topeng saling melengkapi, kita bahkan dapat menggunakan ~ untuk mengganti bit dan menghemat beberapa tinta:

uint32_t reverse_integer_bits(uint32_t b) {
   uint32_t mask = 0b11111111111111110000000000000000;
   b = (b & mask) >> 16 | (b & ~mask) << 16;
   mask = 0b11111111000000001111111100000000;
   b = (b & mask) >> 8 | (b & ~mask) << 8;
   mask = 0b11110000111100001111000011110000;
   b = (b & mask) >> 4 | (b & ~mask) << 4;
   mask = 0b11001100110011001100110011001100;
   b = (b & mask) >> 2 | (b & ~mask) << 2;
   mask = 0b10101010101010101010101010101010;
   b = (b & mask) >> 1 | (b & ~mask) << 1;
   return b;
}

[C ++ Only] Reverse Any Unsigned (Template)

Logika di atas dapat diringkas dengan loop yang akan bekerja pada semua jenis unsigned:

template <class T>
T reverse_bits(T n) {
    short bits = sizeof(n) * 8; 
    T mask = ~T(0); // equivalent to uint32_t mask = 0b11111111111111111111111111111111;

    while (bits >>= 1) {
        mask ^= mask << (bits); // will convert mask to 0b00000000000000001111111111111111;
        n = (n & ~mask) >> bits | (n & mask) << bits; // divide and conquer
    }

    return n;
}

Cobalah sendiri dengan memasukkan fungsi di atas:

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>

template <class T>
void print_binary(T n)
{   T mask = 1ULL << ((sizeof(n) * 8) - 1);  // will set the most significant bit
    for(; mask != 0; mask >>= 1) putchar('0' | !!(n & mask));
    putchar('\n');
}

int main() {
    uint32_t n = 12;
    print_binary(n);
    n = reverse_bits(n); 
    print_binary(n);
    unsigned char c = 'a';
    print_binary(c);
    c = reverse_bits(c);
    print_binary(c);
    uint16_t s = 12;
    print_binary(s);
    s = reverse_bits(s);
    print_binary(s);
    uint64_t l = 12;
    print_binary(l);
    l = reverse_bits(l);
    print_binary(l);
    return 0;
}

Membalik dengan asm volatile

Last but not least, jika yang paling sederhana berarti lebih sedikit baris, mengapa tidak mencoba perakitan inline?

Anda dapat menguji potongan kode di bawah ini dengan menambahkan -masm=intelsaat menyusun:

unsigned char reverse_bits(unsigned char c) {
    __asm__ __volatile__ (R"(
        mov cx, 8       
    daloop:                   
        ror di          
        adc ax, ax      
        dec cx          
        jnz short daloop  
    ;)");
}

Penjelasan baris demi baris:

        mov cx, 8       ; we will reverse the 8 bits contained in one byte
    daloop:             ; while loop
        shr di          ; Shift Register `di` (containing value of the first argument of callee function) to the Right
        rcl ax          ; Rotate Carry Left: rotate ax left and add the carry from shr di, the carry is equal to 1 if one bit was "lost" from previous operation 
        dec cl          ; Decrement cx
        jnz short daloop; Jump if cx register is Not equal to Zero, else end loop and return value contained in ax register
Antonin GAVREL
sumber
3

Pencarian tabel atau

uint8_t rev_byte(uint8_t x) {
    uint8_t y;
    uint8_t m = 1;
    while (m) {
       y >>= 1;
       if (m&x) {
          y |= 0x80;
       }
       m <<=1;
    }
    return y;
}

edit

Lihat di sini untuk solusi lain yang mungkin bekerja lebih baik untuk Anda

nategoose
sumber
3

implementasi yang lebih lambat tetapi lebih sederhana:

static int swap_bit(unsigned char unit)
{
    /*
     * swap bit[7] and bit[0]
     */
    unit = (((((unit & 0x80) >> 7) ^ (unit & 0x01)) << 7) | (unit & 0x7f));
    unit = (((((unit & 0x80) >> 7) ^ (unit & 0x01))) | (unit & 0xfe));
    unit = (((((unit & 0x80) >> 7) ^ (unit & 0x01)) << 7) | (unit & 0x7f));

    /*
     * swap bit[6] and bit[1]
     */
    unit = (((((unit & 0x40) >> 5) ^ (unit & 0x02)) << 5) | (unit & 0xbf));
    unit = (((((unit & 0x40) >> 5) ^ (unit & 0x02))) | (unit & 0xfd));
    unit = (((((unit & 0x40) >> 5) ^ (unit & 0x02)) << 5) | (unit & 0xbf));

    /*
     * swap bit[5] and bit[2]
     */
    unit = (((((unit & 0x20) >> 3) ^ (unit & 0x04)) << 3) | (unit & 0xdf));
    unit = (((((unit & 0x20) >> 3) ^ (unit & 0x04))) | (unit & 0xfb));
    unit = (((((unit & 0x20) >> 3) ^ (unit & 0x04)) << 3) | (unit & 0xdf));

    /*
     * swap bit[4] and bit[3]
     */
    unit = (((((unit & 0x10) >> 1) ^ (unit & 0x08)) << 1) | (unit & 0xef));
    unit = (((((unit & 0x10) >> 1) ^ (unit & 0x08))) | (unit & 0xf7));
    unit = (((((unit & 0x10) >> 1) ^ (unit & 0x08)) << 1) | (unit & 0xef));

    return unit;
}
wenlujon.dll
sumber
3

Bisakah ini menjadi solusi cepat?

int byte_to_be_reversed = 
    ((byte_to_be_reversed>>7)&0x01)|((byte_to_be_reversed>>5)&0x02)|      
    ((byte_to_be_reversed>>3)&0x04)|((byte_to_be_reversed>>1)&0x08)| 
    ((byte_to_be_reversed<<7)&0x80)|((byte_to_be_reversed<<5)&0x40)|
    ((byte_to_be_reversed<<3)&0x20)|((byte_to_be_reversed<<1)&0x10);

Singkirkan kesibukan menggunakan for loop! tetapi para ahli tolong beri tahu saya apakah ini efisien dan lebih cepat?

Jambore
sumber
Ini memiliki waktu eksekusi O (n) daripada O (log₂ n), di mana n adalah jumlah bit (8, 16, 32, 64, dll.). Lihat di tempat lain untuk jawaban yang dieksekusi dalam waktu O (log₂ n).
Todd Lehman
2

Sebelum menerapkan solusi algoritmik apa pun, periksa bahasa assembly untuk arsitektur CPU apa pun yang Anda gunakan. Arsitektur Anda mungkin menyertakan instruksi yang menangani manipulasi bitwise seperti ini (dan apa yang bisa lebih sederhana dari instruksi perakitan tunggal?).

Jika instruksi seperti itu tidak tersedia, maka saya akan menyarankan untuk pergi dengan rute tabel pencarian. Anda dapat menulis skrip / program untuk membuat tabel untuk Anda, dan operasi pencarian akan lebih cepat daripada algoritma pembalikan bit mana pun di sini (dengan biaya harus menyimpan tabel pencarian di suatu tempat).

bta
sumber
2

Fungsi sederhana ini menggunakan mask untuk menguji setiap bit dalam byte masukan dan mentransfernya menjadi keluaran bergeser:

char Reverse_Bits(char input)
{    
    char output = 0;

    for (unsigned char mask = 1; mask > 0; mask <<= 1)
    {
        output <<= 1;

        if (input & mask)
            output |= 1;
    }

    return output;
}
luci88filter.dll
sumber
Masker harus tidak bertanda tangan maaf.
luci88filter
1

Satu ini didasarkan pada salah satu BobStein-VisiBone disediakan

#define reverse_1byte(b)    ( ((uint8_t)b & 0b00000001) ? 0b10000000 : 0 ) | \
                            ( ((uint8_t)b & 0b00000010) ? 0b01000000 : 0 ) | \
                            ( ((uint8_t)b & 0b00000100) ? 0b00100000 : 0 ) | \
                            ( ((uint8_t)b & 0b00001000) ? 0b00010000 : 0 ) | \
                            ( ((uint8_t)b & 0b00010000) ? 0b00001000 : 0 ) | \
                            ( ((uint8_t)b & 0b00100000) ? 0b00000100 : 0 ) | \
                            ( ((uint8_t)b & 0b01000000) ? 0b00000010 : 0 ) | \
                            ( ((uint8_t)b & 0b10000000) ? 0b00000001 : 0 ) 

Saya sangat menyukai yang ini karena kompilator secara otomatis menangani pekerjaan untuk Anda, sehingga tidak memerlukan sumber daya lebih lanjut.

ini juga dapat diperpanjang hingga 16-Bit ...

#define reverse_2byte(b)    ( ((uint16_t)b & 0b0000000000000001) ? 0b1000000000000000 : 0 ) | \
                            ( ((uint16_t)b & 0b0000000000000010) ? 0b0100000000000000 : 0 ) | \
                            ( ((uint16_t)b & 0b0000000000000100) ? 0b0010000000000000 : 0 ) | \
                            ( ((uint16_t)b & 0b0000000000001000) ? 0b0001000000000000 : 0 ) | \
                            ( ((uint16_t)b & 0b0000000000010000) ? 0b0000100000000000 : 0 ) | \
                            ( ((uint16_t)b & 0b0000000000100000) ? 0b0000010000000000 : 0 ) | \
                            ( ((uint16_t)b & 0b0000000001000000) ? 0b0000001000000000 : 0 ) | \
                            ( ((uint16_t)b & 0b0000000010000000) ? 0b0000000100000000 : 0 ) | \
                            ( ((uint16_t)b & 0b0000000100000000) ? 0b0000000010000000 : 0 ) | \
                            ( ((uint16_t)b & 0b0000001000000000) ? 0b0000000001000000 : 0 ) | \
                            ( ((uint16_t)b & 0b0000010000000000) ? 0b0000000000100000 : 0 ) | \
                            ( ((uint16_t)b & 0b0000100000000000) ? 0b0000000000010000 : 0 ) | \
                            ( ((uint16_t)b & 0b0001000000000000) ? 0b0000000000001000 : 0 ) | \
                            ( ((uint16_t)b & 0b0010000000000000) ? 0b0000000000000100 : 0 ) | \
                            ( ((uint16_t)b & 0b0100000000000000) ? 0b0000000000000010 : 0 ) | \
                            ( ((uint16_t)b & 0b1000000000000000) ? 0b0000000000000001 : 0 ) 
Natthapol Vanasrivilai
sumber
Saya akan meletakkan bin dalam tanda kurung jika itu adalah ekspresi yang lebih kompleks daripada satu angka, dan mungkin juga mengganti nama makro menjadi REVERSE_BYTEsebagai petunjuk bahwa Anda mungkin tidak ingin memiliki ekspresi (runtime) yang lebih kompleks di sana. Atau jadikan sebagai fungsi sebaris. (Tapi secara keseluruhan saya suka ini karena cukup sederhana sehingga Anda dapat melakukannya dari memori dengan mudah dengan kemungkinan kesalahan yang sangat kecil.)
Arkku
1

Dengan asumsi bahwa compiler Anda mengizinkan unsigned long long :

unsigned char reverse(unsigned char b) {
  return (b * 0x0202020202ULL & 0x010884422010ULL) % 1023;
}

Ditemukan di sini

mascIT
sumber
1

Jika Anda menggunakan mikrokontroler kecil dan membutuhkan solusi kecepatan tinggi dengan footprint kecil, ini bisa menjadi solusi. Dimungkinkan untuk menggunakannya untuk proyek C, tetapi Anda perlu menambahkan file ini sebagai file assembler * .asm, ke proyek C Anda. Instruksi: Dalam proyek C tambahkan deklarasi ini:

extern uint8_t byte_mirror(uint8_t);

Panggil fungsi ini dari C

byteOutput= byte_mirror(byteInput);

Ini adalah kode, hanya cocok untuk inti 8.051. Dalam register CPU r0 adalah data dari byteInput . Kode rotate right r0 cross carry lalu putar carry kiri ke r1 . Ulangi prosedur ini 8 kali, untuk setiap bit. Kemudian register r1 dikembalikan ke c berfungsi sebagai byteOutput. Pada 8051 inti hanya dimungkinkan untuk memutar akumulator a .

NAME     BYTE_MIRROR
RSEG     RCODE
PUBLIC   byte_mirror              //8051 core        

byte_mirror
    mov r3,#8;
loop:   
    mov a,r0;
    rrc a;
    mov r0,a;    
    mov a,r1;
    rlc a;   
    mov r1,a;
    djnz r3,loop
    mov r0,a
    ret

PROS: Ini adalah tapak kecil, ini adalah kecepatan tinggi CONS: Ini bukan kode yang dapat digunakan kembali, ini hanya untuk 8.051

011101101-> bawa

101101110 <-membawa

Josko Marsic
sumber
Meskipun kode ini mungkin menjawab pertanyaan, akan lebih baik untuk memasukkan beberapa konteks, menjelaskan cara kerjanya dan kapan menggunakannya. Jawaban kode-saja tidak berguna dalam jangka panjang.
fNek
0
  xor ax,ax
  xor bx,bx
  mov cx,8
  mov al,original_byte!
cycle:   shr al,1
  jnc not_inc
  inc bl
not_inc: test cx,cx
  jz,end_cycle
  shl bl,1
  loop cycle
end_cycle:

byte terbalik akan berada di register bl

asm_fan
sumber
3
Dalam konteks lain, itu mungkin jawaban yang adil tetapi pertanyaannya adalah tentang C atau C ++, bukan asm ...
jadsq
0
typedef struct
{
    uint8_t b0:1;
    uint8_t b1:1;
    uint8_t b2:1;
    uint8_t b3:1;
    uint8_t b4:1;
    uint8_t b5:1;
    uint8_t b6:1;
    uint8_t b7:1;
} bits_t;

uint8_t reverse_bits(uint8_t src)
{
    uint8_t dst = 0x0;
    bits_t *src_bits = (bits_t *)&src;
    bits_t *dst_bits = (bits_t *)&dst;

    dst_bits->b0 = src_bits->b7;
    dst_bits->b1 = src_bits->b6;
    dst_bits->b2 = src_bits->b5;
    dst_bits->b3 = src_bits->b4;
    dst_bits->b4 = src_bits->b3;
    dst_bits->b5 = src_bits->b2;
    dst_bits->b6 = src_bits->b1;
    dst_bits->b7 = src_bits->b0;

    return dst;
}
Tai-Yuan Fang
sumber
Sebagai catatan gaya, saya menemukan penggunaan uint8_tuntuk bidang 1-bit agak jelek, karena tampaknya pertama kali mengatakan bahwa itu akan memakan waktu 8 bit tetapi kemudian di akhir baris mendefinisikannya hanya sebagai satu bit. Saya akan menggunakan unsigned b0:1dll.
Arkku
0
#include <stdio.h>
#include <stdlib.h>

int main()
{
    int i;
    unsigned char rev = 0x70 ; // 0b01110000
    unsigned char tmp = 0;

    for(i=0;i<8;i++)
    {
    tmp |= ( ((rev & (1<<i))?1:0) << (7-i));
    }
    rev = tmp;

    printf("%x", rev);       //0b00001110 binary value of given number
    return 0;
}
AHMED ANWAR
sumber
Mohon tambahkan penjelasan.
zcui93
0

Saya rasa ini cukup sederhana

uint8_t reverse(uint8_t a)
{
  unsigned w = ((a << 7) & 0x0880) | ((a << 5) & 0x0440) | ((a << 3) & 0x0220) | ((a << 1) & 0x0110);
  return static_cast<uint8_t>(w | (w>>8));
}

atau

uint8_t reverse(uint8_t a)
{
  unsigned w = ((a & 0x11) << 7) | ((a & 0x22) << 5) | ((a & 0x44) << 3) | ((a & 0x88) << 1);
  return static_cast<uint8_t>(w | (w>>8));
}
agbinfo.dll
sumber
0
unsigned char c ; // the original
unsigned char u = // the reversed
c>>7&0b00000001 |
c<<7&0b10000000 |
c>>5&0b00000010 |
c<<5&0b01000000 |
c>>3&0b00000100 |
c<<3&0b00100000 |
c>>1&0b00001000 |
c<<1&0b00010000 ;

Explanation: exchanged bits as per the arrows below.
01234567
<------>
#<---->#
##<-->##
###<>###
Mahen
sumber
0

Saya akan chip dalam solusi saya, karena saya tidak dapat menemukan yang seperti ini dalam jawaban sejauh ini. Ini mungkin sedikit overengineered, tetapi menghasilkan tabel lookup menggunakan C ++ 14 std::index_sequencedalam waktu kompilasi.

#include <array>
#include <utility>

constexpr unsigned long reverse(uint8_t value) {
    uint8_t result = 0;
    for (std::size_t i = 0, j = 7; i < 8; ++i, --j) {
        result |= ((value & (1 << j)) >> j) << i;
    }
    return result;
}

template<size_t... I>
constexpr auto make_lookup_table(std::index_sequence<I...>)
{
    return std::array<uint8_t, sizeof...(I)>{reverse(I)...};   
}

template<typename Indices = std::make_index_sequence<256>>
constexpr auto bit_reverse_lookup_table()
{
    return make_lookup_table(Indices{});
}

constexpr auto lookup = bit_reverse_lookup_table();

int main(int argc)
{
    return lookup[argc];
}

https://godbolt.org/z/cSuWhF

K. Kirsz
sumber
0

Berikut adalah solusi sederhana dan dapat dibaca, portabel untuk semua platform yang sesuai, termasuk yang memiliki sizeof(char) == sizeof(int):

#include <limits.h>

unsigned char reverse(unsigned char c) {
    int shift;
    unsigned char result = 0;

    for (shift = 0; shift < CHAR_BIT; shift++) {
        result <<= 1;
        result |= c & 1;
        c >>= 1;
    }
    return result;
}
chqrlie.dll
sumber
0

Saya tahu bahwa pertanyaan ini kuno, tetapi saya masih berpikir bahwa topik tersebut relevan untuk beberapa tujuan, dan ini adalah versi yang berfungsi dengan sangat baik dan dapat dibaca. Saya tidak bisa mengatakan bahwa ini adalah yang tercepat atau paling efisien, tetapi harus menjadi salah satu yang terbersih. Saya juga menyertakan fungsi helper untuk menampilkan pola bit dengan mudah. Fungsi ini menggunakan beberapa fungsi pustaka standar daripada menulis manipulator bit Anda sendiri.

#include <algorithm>
#include <bitset>
#include <exception>
#include <iostream>
#include <limits>
#include <string>

// helper lambda function template
template<typename T>
auto getBits = [](T value) {
    return std::bitset<sizeof(T) * CHAR_BIT>{value};
};

// Function template to flip the bits
// This will work on integral types such as int, unsigned int,
// std::uint8_t, 16_t etc. I did not test this with floating
// point types. I chose to use the `bitset` here to convert
// from T to string as I find it easier to use than some of the
// string to type or type to string conversion functions,
// especially when the bitset has a function to return a string. 
template<typename T>
T reverseBits(T& value) {
    static constexpr std::uint16_t bit_count = sizeof(T) * CHAR_BIT;

    // Do not use the helper function in this function!
    auto bits = std::bitset<bit_count>{value};
    auto str = bits.to_string();
    std::reverse(str.begin(), str.end());
    bits = std::bitset<bit_count>(str);
    return static_cast<T>( bits.to_ullong() );
}

// main program
int main() {
    try {
        std::uint8_t value = 0xE0; // 1110 0000;
        std::cout << +value << '\n'; // don't forget to promote unsigned char
        // Here is where I use the helper function to display the bit pattern
        auto bits = getBits<std::uint8_t>(value);
        std::cout << bits.to_string() << '\n';

        value = reverseBits(value);
        std::cout << +value << '\n'; // + for integer promotion

        // using helper function again...
        bits = getBits<std::uint8_t>(value);
        std::cout << bits.to_string() << '\n';

    } catch(const std::exception& e) {  
        std::cerr << e.what();
        return EXIT_FAILURE;
    }
    return EXIT_SUCCESS;
}

Dan itu memberikan hasil sebagai berikut.

224
11100000
7
00000111
Francis Cugler
sumber
0

Yang ini membantu saya dengan set array 8x8 dot matrix.

uint8_t mirror_bits(uint8_t var)
{
    uint8_t temp = 0;
    if ((var & 0x01))temp |= 0x80;
    if ((var & 0x02))temp |= 0x40;
    if ((var & 0x04))temp |= 0x20;
    if ((var & 0x08))temp |= 0x10;

    if ((var & 0x10))temp |= 0x08;
    if ((var & 0x20))temp |= 0x04;
    if ((var & 0x40))temp |= 0x02;
    if ((var & 0x80))temp |= 0x01;

    return temp;
}
R1S8K
sumber
1
Fungsi ini sebenarnya tidak berfungsi, kebalikan dari 0b11001111 seharusnya 0b11110011, tetapi gagal dengan fungsi ini. Metode pengujian yang sama berfungsi untuk banyak fungsi lain yang tercantum di sini.
Dan
Ya, terima kasih saya mengoreksi jawaban saya. Terima kasih telah memberi tahu saya tentang kesalahan saya :)
R1S8K