ใน C / C ++ วิธีที่ง่ายที่สุดในการย้อนกลับลำดับของบิตเป็นไบต์คืออะไร?


110

แม้ว่าจะมีหลายวิธีในการย้อนกลับลำดับบิตในแต่ละไบต์ แต่ฉันก็อยากรู้ว่าอะไร "ง่ายที่สุด" สำหรับนักพัฒนาในการนำไปใช้ และโดยการย้อนกลับฉันหมายถึง:

1110 -> 0111
0010 -> 0100

คำถามนี้คล้ายกับ แต่ไม่ซ้ำกับคำถาม PHP นี้

คำถามนี้คล้ายกับ แต่ไม่ซ้ำกับคำถาม C นี้ คำถามนี้กำลังถามหาวิธีที่ง่ายที่สุดในการนำไปใช้โดยนักพัฒนา "อัลกอริทึมที่ดีที่สุด" เกี่ยวข้องกับหน่วยความจำและประสิทธิภาพของซีพียู


ใช้การประกอบแบบอินไลน์ ดีกว่าใส่ฟังก์ชันลงในหน่วยการแปลแยกต่างหาก มีโมดูลภาษาแอสเซมบลีหนึ่งโมดูลสำหรับแต่ละแพลตฟอร์มเป้าหมาย ให้กระบวนการสร้างเลือกโมดูล
Thomas Matthews

@Andreas การใช้งานที่ง่ายที่สุด
ธาน

ที่เกี่ยวข้อง: codegolf.stackexchange.com/questions/36213/…
MM

คำตอบ:


102

หากคุณกำลังพูดถึงไบต์เดียวการค้นหาตารางน่าจะเป็นทางออกที่ดีที่สุดเว้นแต่คุณจะไม่มี 256 ไบต์ด้วยเหตุผลบางประการ


12
หากเรากำลังพูดถึงสิ่งที่ง่ายต่อการนำไปใช้โดยไม่ต้องคัดลอกโซลูชันสำเร็จรูปการสร้างตารางการค้นหายังคงต้องการโซลูชันอื่น (แน่นอนว่าอาจทำด้วยมือ แต่ก็เกิดข้อผิดพลาดได้ง่ายและใช้เวลานาน…)
Arkku

7
คุณสามารถบีบอาร์เรย์ให้เหลือน้อยกว่า 256 ไบต์ได้หากคุณไม่สนใจ palindromes
wilhelmtell

8
@wilhelmtell - คุณต้องมีตารางเพื่อที่จะรู้ว่าอันไหนคือ Palindromes
Mark Ransom

6
@wilhelmtell: ในการเขียนสคริปต์เรายังต้องการวิธีแก้ปัญหาอื่นซึ่งเป็นประเด็นของฉัน - ตารางการค้นหาใช้งานง่าย แต่ไม่ง่ายในการสร้าง (ยกเว้นโดยการคัดลอกตารางการค้นหาสำเร็จรูป แต่อาจมีการคัดลอกวิธีแก้ปัญหาใด ๆ ก็ได้เช่นกัน) ตัวอย่างเช่นหากวิธีการแก้ปัญหาที่ "ง่ายที่สุด" ถือเป็นวิธีที่สามารถเขียนลงบนกระดาษในการสอบหรือสัมภาษณ์ได้ฉันจะไม่ เริ่มสร้างตารางค้นหาด้วยมือและสร้างโปรแกรมที่จะทำมันจะมีโซลูชันที่แตกต่างกันอยู่แล้ว (ซึ่งจะง่ายกว่าเพียงอย่างเดียวรวมทั้งตารางและตาราง)
Arkku

4
@Arkku สิ่งที่ฉันหมายถึงคือเขียนสคริปต์ที่ส่งออกตารางของ 256 ไบต์แรกและการแม็ปย้อนกลับ ใช่คุณกลับไปเขียนฟังก์ชันย้อนกลับ แต่ตอนนี้เป็นภาษาสคริปต์ที่คุณชื่นชอบและอาจเป็นเรื่องที่น่ารังเกียจเท่าที่คุณต้องการคุณจะทิ้งมันทันทีที่เสร็จสิ้นและคุณเรียกใช้ครั้งเดียว มีการส่งออกของสคริปต์รหัส C unsigned int rtable[] = {0x800, 0x4000, ...};แม้: จากนั้นทิ้งสคริปต์และลืมว่าคุณเคยมีมัน เขียนได้เร็วกว่าโค้ด C ++ ที่เทียบเท่ามากและจะทำงานเพียงครั้งเดียวดังนั้นคุณจะได้รับรันไทม์ O (1) ในโค้ด C ++ ของคุณ
wilhelmtell

227

สิ่งนี้ควรใช้งานได้:

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

ก่อนอื่นให้สลับสี่บิตทางซ้ายกับสี่บิตทางขวา จากนั้นคู่ที่อยู่ติดกันทั้งหมดจะถูกสลับจากนั้นบิตเดี่ยวที่อยู่ติดกันทั้งหมด ซึ่งส่งผลให้มีการกลับคำสั่งซื้อ


26
สั้นและรวดเร็วพอสมควร แต่ไม่ง่าย
Mark Ransom

3
วิธีนี้ยังรวมถึงการแลกเปลี่ยนไบต์สำหรับ endianness
Boojum

2
ไม่ใช่แนวทางที่ง่ายที่สุด แต่ฉันชอบ +1
ธาน

8
ใช่มันเป็นเรื่องง่าย มันเป็นขั้นตอนวิธีการแบ่งและพิชิต ยอดเยี่ยม!
kiewic

เร็วกว่าวิธีที่ @Arkku แนะนำด้านล่างหรือไม่?
qed

123

ฉันคิดว่าตารางการค้นหาต้องเป็นหนึ่งในวิธีที่ง่ายที่สุด อย่างไรก็ตามคุณไม่จำเป็นต้องมีตารางการค้นหาแบบเต็ม

//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]

สิ่งนี้ค่อนข้างง่ายในการเขียนโค้ดและตรวจสอบด้วยสายตา
ท้ายที่สุดแล้วสิ่งนี้อาจเร็วกว่าโต๊ะเต็มด้วยซ้ำ บิตอาริ ธ มีราคาถูกและตารางพอดีกับแคชไลน์


10
นั่นเป็นวิธีที่ดีเยี่ยมในการลดความซับซ้อนของโซลูชันตาราง +1

3
ดี แต่จะทำให้คุณพลาดแคช
Johan Kotlinski

7
@kotlinski: อะไรจะทำให้แคชพลาด? ฉันคิดว่าเวอร์ชันตารางขนาดเล็กอาจมีประสิทธิภาพในการแคชมากกว่ารุ่นใหญ่ ใน Core2 ของฉันบรรทัดแคชกว้าง 64 ไบต์ตารางเต็มจะครอบคลุมหลายบรรทัดในขณะที่ตารางขนาดเล็กจะพอดีกับบรรทัดเดียวได้อย่างง่ายดาย
deft_code

4
@kotlinski: พื้นที่ชั่วคราวมีความสำคัญมากกว่าสำหรับแคชฮิตหรือกลยุทธ์การแทนที่มากกว่าที่อยู่ในท้องที่
cfi

6
@Harshdeep: พิจารณาดัชนีที่เข้ารหัสไบนารีของรายการตาราง ดัชนี b0000 (0) -> b0000 (0x0) น่าเบื่อ; b0001(1) -> b1000(0x8), ,b0010(2) -> b0100(0x4) b1010(10) -> b0101(0x5)ดูรูปแบบ? มันง่ายพอที่คุณจะคำนวณมันในหัวของคุณได้ (ถ้าคุณสามารถอ่านไบนารีได้มิฉะนั้นคุณจะต้องใช้กระดาษในการคำนวณ) สำหรับการก้าวกระโดดที่การย้อนกลับจำนวนเต็ม 8 บิตจะเหมือนกับการย้อนกลับส่วน 4 บิตจากนั้นทำการสลับ ฉันอ้างว่ามีประสบการณ์และสัญชาตญาณ (หรือเวทมนตร์)
deft_code

46

ดูการแฮ็กที่น่าทึ่งสำหรับโซลูชันมากมาย การคัดลอกจากที่นั่นทำได้ง่ายมาก =)

ตัวอย่างเช่น (บน CPU 32 บิต):

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

หาก "ใช้งานง่าย" หมายถึงสิ่งที่สามารถทำได้โดยไม่ต้องอ้างอิงในการสอบหรือสัมภาษณ์งานการเดิมพันที่ปลอดภัยที่สุดน่าจะเป็นการคัดลอกบิตทีละตัวไปยังตัวแปรอื่นในลำดับที่กลับกันอย่างไม่มีประสิทธิภาพ (แสดงไว้แล้วในคำตอบอื่น ๆ ).


1
จาก URL ของคุณ: CPU 32 บิต: b = ((b * 0x0802LU & 0x22110LU) | (b * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16;
Joshua

1
@ โจชัว: นั่นเป็นสิ่งที่ฉันชอบมากเช่นกัน ข้อแม้ (ตามที่ระบุไว้ในหน้าที่เชื่อมโยง) คือต้องกำหนดหรือโยนลงใน uint8_t มิฉะนั้นจะมีขยะอยู่ในบิตด้านบน
Arkku

42

เนื่องจากไม่มีใครโพสต์โซลูชันการค้นหาตารางที่สมบูรณ์นี่จึงเป็นของฉัน:

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

2
มีประโยชน์ขอบคุณ ดูเหมือนว่าวิธีการเปลี่ยนเกียร์ที่ช้าลงของฉันกำลัง จำกัด ประสิทธิภาพในแอปแบบฝัง วางตารางใน ROM บน PIC (พร้อมคำหลัก rom)
เดินทาง

1
วิธีการที่ง่ายกว่า: graphics.stanford.edu/~seander/bithacks.html#BitReverseTable
พักใน

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

แก้ไข:

แปลงเป็นเทมเพลตด้วย bitcount เสริม


@nvl - แก้ไขแล้ว ผมเริ่มสร้างมันเป็นแม่แบบ แต่ตัดสินใจที่ผ่านไปครึ่งทางที่จะไม่ทำเช่นนั้น ... มากเกินไป & gt & lt
andand

สำหรับ pedenatry เสริมแทนที่ด้วยsizeof(T)*8 sizeof(T)*CHAR_BITS
Pillsy

6
@andand สำหรับจี้พิเศษพิเศษแทนที่sizeof(T)*CHAR_BITด้วยstd::numeric_limits<T>::digits(เกือบ 4 ปีของการอวดรู้ในภายหลัง)
Morwenn

1
มันควรจะเป็นไม่ได้CHAR_BIT CHAR_BITS
Xunie

1
ควรเป็น rv = (rv << 1) | (n & 0x01);
Vignesh

16

สองบรรทัด:

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

หรือในกรณีที่คุณมีปัญหากับส่วน "0b1":

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

"original" คือไบต์ที่คุณต้องการย้อนกลับ "ย้อนกลับ" คือผลลัพธ์เริ่มต้นเป็น 0


14

แม้ว่าอาจจะไม่พกพา แต่ฉันก็ใช้ภาษาแอสเซมบลี
ภาษาแอสเซมบลีจำนวนมากมีคำแนะนำในการหมุนเล็กน้อยในแฟล็กพกพาและหมุนแฟล็กพกพาเป็นคำ (หรือไบต์)

อัลกอริทึมคือ:

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

รหัสภาษาระดับสูงสำหรับสิ่งนี้ซับซ้อนกว่ามากเนื่องจาก C และ C ++ ไม่รองรับการหมุนเพื่อดำเนินการและหมุนจากการพกพา ธงพกต้องเป็นแบบจำลอง

แก้ไข: ภาษาแอสเซมบลีตัวอย่างเช่น

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

7
ฉันคิดว่าคำตอบนี้ตรงกันข้ามกับคำตอบง่ายๆ ไม่พกพาประกอบและซับซ้อนพอที่จะเขียนด้วยรหัสหลอกแทนการประกอบจริง
deft_code

3
มันค่อนข้างง่าย ฉันใส่มันลงในรหัสหลอกเพราะการจำแอสเซมบลีนั้นเฉพาะสำหรับโปรเซสเซอร์สายพันธุ์และมีหลายสายพันธุ์อยู่ที่นั่น หากคุณต้องการฉันสามารถแก้ไขได้เพื่อแสดงภาษาแอสเซมบลีอย่างง่าย
Thomas Matthews

เราสามารถดูได้ว่าการเพิ่มประสิทธิภาพคอมไพลเลอร์ช่วยลดความซับซ้อนในคำสั่งประกอบที่เหมาะสมหรือไม่
Sparky

12

ฉันพบว่าวิธีแก้ปัญหาต่อไปนี้ง่ายกว่าอัลกอริทึมการเล่นซอบิตอื่น ๆ ที่ฉันเคยเห็นที่นี่

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

ได้รับทุกบิตในไบต์และเลื่อนไปตามลำดับโดยเริ่มจากตัวแรกถึงตัวสุดท้าย

คำอธิบาย:

   ((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

สวย! ที่ชื่นชอบจนถึงตอนนี้
Nick Rameau

นี่เป็นเรื่องง่ายอย่างแน่นอน แต่ควรชี้ให้เห็นว่าเวลาดำเนินการคือ O (n) มากกว่า O (log₂ n) โดยที่ n คือจำนวนบิต (8, 16, 32, 64, ฯลฯ )
Todd Lehman

10

ง่ายวิธีที่น่าจะเป็นที่จะย้ำกว่าตำแหน่งบิตในวง:

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

เป็น CHAR_BIT โดยไม่มี 's'
ljrk

ทำไมต้องใช้CHAR_BITเมื่อคุณคิดว่าcharมี 8 บิต?
chqrlie

6

คุณอาจสนใจstd::vector<bool>(ที่บรรจุบิต) และstd::bitset

ควรจะง่ายที่สุดตามที่ขอ

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

ตัวเลือกอื่น ๆ อาจเร็วกว่า

แก้ไข: ฉันเป็นหนี้คุณโดยใช้วิธีแก้ปัญหา 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;
}

ตัวอย่างที่สองต้องการส่วนขยาย c ++ 0x (เพื่อเริ่มต้นอาร์เรย์ด้วย{...}) ข้อดีของการใช้ a bitsetหรือ a std::vector<bool>(หรือ a boost::dynamic_bitset) คือคุณไม่ จำกัด เฉพาะไบต์หรือคำ แต่สามารถย้อนกลับจำนวนบิตได้ตามอำเภอใจ

HTH


บิตเซ็ตง่ายกว่าพ็อดอย่างไร แสดงรหัสหรือไม่ใช่
wilhelmtell

ฉันคิดว่ารหัสนั้นจะย้อนกลับบิตเซ็ตแล้วย้อนกลับไปเป็นแบบเดิม เปลี่ยนii! = size (); ถึงii <size () / 2; และมันจะทำงานได้ดีขึ้น =)
Viktor Sehr

(@ viktor-sehr ไม่มันจะไม่ rev แตกต่างจาก bs) อย่างไรก็ตามฉันไม่ชอบคำตอบของตัวเอง: ฉันคิดว่านี่เป็นกรณีที่ตัวดำเนินการเลขคณิตไบนารีและตัวดำเนินการกะเหมาะสมกว่า มันยังคงเป็นสิ่งที่เข้าใจง่ายที่สุด
baol

แล้วstd::vector<bool> b = { ... }; std::vector<bool> rb ( b.rbegin(), b.rend()); - ใช้ reverse iterators โดยตรงได้อย่างไร?
MSalters

@MSalters ฉันชอบความไม่เปลี่ยนรูปของมัน
baol

6

สำหรับกรณีค่าคงที่อินพุต8 บิตที่จำกัด มากวิธีนี้ไม่มีค่าใช้จ่ายหน่วยความจำหรือ CPU ในขณะทำงาน:

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

ฉันใช้สิ่งนี้สำหรับ ARINC-429 โดยลำดับบิต (endianness) ของป้ายกำกับอยู่ตรงข้ามกับส่วนที่เหลือของคำ ฉลากมักเป็นค่าคงที่และตามอัตภาพเป็นฐานแปด

นี่คือวิธีที่ฉันใช้เพื่อกำหนดค่าคงที่เนื่องจากข้อมูลจำเพาะกำหนดป้ายกำกับนี้เป็นบิ๊กเอนเดียน 205 ฐานแปด

#define LABEL_HF_COMM MSB2LSB(0205)

ตัวอย่างเพิ่มเติม:

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

5

มีหลายวิธีในการย้อนกลับบิตขึ้นอยู่กับว่าคุณหมายถึงอะไร "วิธีที่ง่ายที่สุด"


ย้อนกลับโดยการหมุน

อาจเป็นตรรกะที่สุดประกอบด้วยการหมุนไบต์ในขณะที่ใช้มาสก์ในบิตแรก(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) เนื่องจากความยาวของถ่านที่ไม่ได้ลงนามคือ 1 ไบต์ซึ่งเท่ากับ 8 บิตหมายความว่าเราจะสแกนแต่ละบิต while (byte_len--)

2) เราตรวจสอบก่อนว่า b อยู่ทางขวาสุดด้วย(b & 1); ถ้าเป็นเช่นนั้นเราตั้งบิต 1 บน r ด้วย|และเลื่อนไปทางซ้ายเพียง 1 บิตโดยการคูณ r ด้วย 2 ด้วย(r << 1)

3) จากนั้นเราหารถ่าน b ที่ไม่ได้ลงชื่อด้วย 2 ด้วยb >>=1เพื่อลบบิตที่อยู่ทางขวาสุดของตัวแปร b เพื่อเป็นการเตือนว่า b >> = 1; เทียบเท่ากับ b / = 2;


ย้อนกลับในบรรทัดเดียว

โซลูชันนี้มาจากRich Schroeppel ในส่วน Programming Hacks

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

1) การดำเนินการทวีคูณ (b * 0x0202020202ULL) สร้างสำเนาห้าชุดของรูปแบบไบต์ 8 บิตแยกกันเพื่อขยายเป็นค่า 64 บิต

2) การดำเนินการ AND (& 0x010884422010ULL) เลือกบิตที่อยู่ในตำแหน่งที่ถูกต้อง (ย้อนกลับ) โดยสัมพันธ์กับกลุ่มบิต 10 บิตแต่ละกลุ่ม

3) ร่วมกันการคูณและการดำเนินการ AND คัดลอกบิตจากไบต์ดั้งเดิมเพื่อให้แต่ละชุดปรากฏในชุด 10 บิตเพียงชุดเดียว ตำแหน่งที่กลับด้านของบิตจากไบต์เดิมตรงกับตำแหน่งสัมพัทธ์ภายในชุด 10 บิตใด ๆ

4) ขั้นตอนสุดท้าย (% 0x3ff) ซึ่งเกี่ยวข้องกับการหารโมดูลัสด้วย 2 ^ 10 - 1 มีผลของการรวมชุดละ 10 บิตเข้าด้วยกัน (จากตำแหน่ง 0-9, 10-19, 20-29, ... ) ในค่า 64 บิต พวกเขาไม่ทับซ้อนกันดังนั้นขั้นตอนเพิ่มเติมที่อยู่ภายใต้การแบ่งโมดูลัสจะทำงานเหมือนการดำเนินการหรือ


หารและพิชิตโซลูชัน

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

นี่เป็นคำตอบที่ได้รับการโหวตมากที่สุดและแม้จะมีคำอธิบาย แต่ฉันคิดว่าสำหรับคนส่วนใหญ่แล้วมันยากที่จะเห็นภาพว่า 0xF0, 0xCC, 0xAA, 0x0F, 0x33 และ 0x55 หมายถึงอะไรอย่างแท้จริง

ไม่ได้ใช้ประโยชน์จาก '0b' ซึ่งเป็นส่วนขยาย GCCและรวมอยู่ในมาตรฐาน C ++ 14 ซึ่งวางจำหน่ายในเดือนธันวาคม 2014 หลังจากนั้นไม่นานหลังจากคำตอบนี้ตั้งแต่เดือนเมษายน 2010

ค่าคงที่จำนวนเต็มสามารถเขียนเป็นค่าคงที่แบบไบนารีซึ่งประกอบด้วยลำดับของตัวเลข '0' และ '1' ซึ่งนำหน้าด้วย '0b' หรือ '0B' สิ่งนี้มีประโยชน์อย่างยิ่งในสภาพแวดล้อมที่ใช้งานได้มากในระดับบิต (เช่นไมโครคอนโทรลเลอร์)

โปรดตรวจสอบข้อมูลโค้ดด้านล่างเพื่อจดจำและทำความเข้าใจวิธีแก้ปัญหานี้ให้ดียิ่งขึ้นโดยที่เราย้ายทีละครึ่ง:

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

หมายเหตุ: >> 4เนื่องจากมี 8 บิตใน 1 ไบต์ซึ่งเป็นถ่านที่ไม่ได้ลงชื่อดังนั้นเราจึงต้องการนำอีกครึ่งหนึ่งไปเรื่อย ๆ

เราสามารถใช้โซลูชันนี้กับ 4 ไบต์ได้อย่างง่ายดายโดยมีเพียงสองบรรทัดเพิ่มเติมและทำตามตรรกะเดียวกัน เนื่องจากทั้งสองมาสก์เสริมซึ่งกันและกันเราจึงสามารถใช้ ~ เพื่อสลับบิตและประหยัดหมึกได้:

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 ++ เท่านั้น] ย้อนกลับใด ๆ ที่ไม่ได้ลงนาม (เทมเพลต)

ตรรกะข้างต้นสามารถสรุปได้ด้วยลูปที่สามารถใช้ได้กับประเภทใด ๆ ที่ไม่ได้ลงนาม:

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

ลองด้วยตัวคุณเองโดยรวมฟังก์ชันข้างต้น:

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

ย้อนกลับด้วย asm volatile

สุดท้าย แต่ไม่ท้ายสุดถ้าง่ายที่สุดหมายถึงเส้นน้อยลงทำไมไม่ลองประกอบอินไลน์ดูล่ะ?

คุณสามารถทดสอบข้อมูลโค้ดด้านล่างได้โดยเพิ่ม-masm=intelเมื่อคอมไพล์:

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

คำอธิบายทีละบรรทัด:

        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

3

การค้นหาตารางหรือ

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

แก้ไข

ดูที่นี่สำหรับการแก้ปัญหาอื่น ๆ ที่อาจทำงานได้ดีขึ้นสำหรับคุณ


3

การใช้งานที่ช้าลง แต่ง่ายกว่า:

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

3

วิธีนี้สามารถแก้ปัญหาได้อย่างรวดเร็วหรือไม่?

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

กำจัดความเร่งรีบในการใช้ for loop! แต่ผู้เชี่ยวชาญช่วยบอกฉันทีว่ามีประสิทธิภาพและเร็วกว่านี้ไหม


มีเวลาดำเนินการคือ O (n) แทนที่จะเป็น O (log₂ n) โดยที่ n คือจำนวนบิต (8, 16, 32, 64 ฯลฯ ) ดูที่อื่นสำหรับคำตอบที่ดำเนินการในเวลา O (log₂ n)
Todd Lehman

2

ก่อนใช้โซลูชันอัลกอริทึมใด ๆ ให้ตรวจสอบภาษาแอสเซมบลีสำหรับสถาปัตยกรรม CPU ที่คุณใช้อยู่ สถาปัตยกรรมของคุณอาจมีคำแนะนำที่จัดการกับการปรับแต่งแบบบิตเช่นนี้ (และอะไรจะง่ายกว่าคำสั่งประกอบเดี่ยว?)

หากไม่มีคำแนะนำดังกล่าวฉันขอแนะนำให้ใช้เส้นทางตารางการค้นหา คุณสามารถเขียนสคริปต์ / โปรแกรมเพื่อสร้างตารางให้คุณได้และการดำเนินการค้นหาจะเร็วกว่าอัลกอริทึมการย้อนกลับบิตใด ๆ ที่นี่ (มีค่าใช้จ่ายในการจัดเก็บตารางการค้นหาไว้ที่ใดที่หนึ่ง)


2

ฟังก์ชันง่ายๆนี้ใช้มาสก์เพื่อทดสอบแต่ละบิตในไบต์อินพุตและถ่ายโอนไปยังเอาท์พุตการเปลี่ยนเกียร์:

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

1

อันนี้อ้างอิงจากBobStein-VisiBone ที่ให้มา

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

ฉันชอบอันนี้มากเพราะคอมไพเลอร์จะจัดการงานให้คุณโดยอัตโนมัติจึงไม่ต้องใช้ทรัพยากรเพิ่มเติม

นอกจากนี้ยังสามารถขยายได้ถึง 16 บิต ...

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

ฉันจะใส่bในวงเล็บในกรณีที่เป็นนิพจน์ที่ซับซ้อนกว่าตัวเลขเดียวและอาจเปลี่ยนชื่อมาโครREVERSE_BYTEเป็นคำใบ้ว่าคุณอาจไม่ต้องการให้มีนิพจน์ (รันไทม์) ที่ซับซ้อนมากขึ้นที่นั่น หรือทำให้เป็นฟังก์ชันอินไลน์ (แต่โดยรวมแล้วฉันชอบสิ่งนี้ที่เรียบง่ายพอที่คุณสามารถทำได้จากหน่วยความจำอย่างง่ายดายโดยมีโอกาสผิดพลาดน้อยมาก)
Arkku


1

หากคุณใช้ไมโครคอนโทรลเลอร์ขนาดเล็กและต้องการโซลูชันความเร็วสูงที่มีขนาดเล็กนี่อาจเป็นวิธีแก้ปัญหา เป็นไปได้ที่จะใช้สำหรับโปรเจ็กต์ C แต่คุณต้องเพิ่มไฟล์นี้เป็นไฟล์แอสเซมเบลอร์ * .asm ในโปรเจ็กต์ C ของคุณ คำแนะนำ: ในโครงการ C เพิ่มการประกาศนี้:

extern uint8_t byte_mirror(uint8_t);

เรียกใช้ฟังก์ชันนี้จาก C

byteOutput= byte_mirror(byteInput);

นี่คือรหัสเหมาะสำหรับแกน 8051 เท่านั้น ในการลงทะเบียนของ CPU R0เป็นข้อมูลจากbyteInput รหัสหมุนขวาr0แบกกางเขนแล้วพกหมุนซ้ายไปr1 ทำซ้ำขั้นตอนนี้ 8 ครั้งทุกบิต จากนั้นรีจิสเตอร์ r1 จะถูกส่งกลับไปยังฟังก์ชัน c เป็น byteOutput ในแกน 8051 สามารถหมุน acumulator 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

ข้อดี: เป็นรอยขนาดเล็กเป็นความเร็วสูงข้อเสีย: ไม่ใช่รหัสที่ใช้ซ้ำได้สำหรับ 8051 เท่านั้น

011101101-> พก

101101110 <- ดำเนินการ


แม้ว่ารหัสนี้อาจตอบคำถามได้ แต่จะเป็นการดีกว่าที่จะรวมบริบทบางส่วนอธิบายวิธีการทำงานและเวลาที่ควรใช้ คำตอบแบบใช้รหัสเท่านั้นไม่มีประโยชน์ในระยะยาว
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:

ไบต์ที่กลับด้านจะอยู่ที่bl register


3
ในบริบทอื่นที่อาจเป็นคำตอบที่ยุติธรรม แต่คำถามเกี่ยวกับ C หรือ C ++ ไม่ใช่ 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;
}

ในฐานะที่เป็นโวหารฉันพบว่าการใช้uint8_tฟิลด์ 1 บิตค่อนข้างน่าเกลียดเนื่องจากก่อนอื่นบอกว่าจะใช้เวลา 8 บิต แต่ในตอนท้ายของบรรทัดกำหนดว่าเป็นเพียงบิตเดียว ฉันจะใช้unsigned b0:1ฯลฯ
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;
}

กรุณาเพิ่มคำอธิบาย
zcui93

0

ฉันคิดว่ามันง่ายพอ

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

หรือ

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

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

0

ฉันจะชิปในการแก้ปัญหาของฉันเนื่องจากฉันไม่พบอะไรแบบนี้ในคำตอบจนถึงตอนนี้ มันอาจจะใช้งานน้อยเกินไป แต่มันสร้างตารางการค้นหาโดยใช้ C ++ 14 std::index_sequenceในเวลาคอมไพล์

#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


0

นี่คือโซลูชันที่ง่ายและอ่านได้พกพาไปยังแพลตฟอร์มที่สอดคล้องกันทั้งหมดรวมถึงโซลูชันที่มี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;
}

0

ฉันรู้ว่าคำถามนี้ล้าสมัย แต่ฉันยังคิดว่าหัวข้อนี้เกี่ยวข้องกับวัตถุประสงค์บางประการและนี่คือเวอร์ชันที่ใช้งานได้ดีและอ่านได้ ฉันไม่สามารถพูดได้ว่าเร็วที่สุดหรือมีประสิทธิภาพมากที่สุด แต่ควรเป็นหนึ่งในสิ่งที่สะอาดที่สุด ฉันได้รวมฟังก์ชั่นตัวช่วยสำหรับการแสดงรูปแบบบิตอย่างง่ายดาย ฟังก์ชันนี้ใช้ฟังก์ชันไลบรารีมาตรฐานบางอย่างแทนการเขียนตัวจัดการบิตของคุณเอง

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

และให้ผลลัพธ์ดังต่อไปนี้

224
11100000
7
00000111

0

อันนี้ช่วยฉันด้วยชุดอาร์เรย์ดอทเมทริกซ์ 8x8

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

1
ฟังก์ชั่นนี้ใช้งานไม่ได้จริงการย้อนกลับของ 0b11001111 ควรเป็น 0b11110011 แต่ล้มเหลวด้วยฟังก์ชันนี้ วิธีการทดสอบเดียวกันนี้ใช้ได้กับฟังก์ชันอื่น ๆ ที่แสดงไว้ที่นี่
แดน

ใช่ขอบคุณฉันแก้ไขคำตอบของฉันแล้ว ขอบคุณที่แจ้งให้เราทราบเกี่ยวกับความผิดพลาดของฉัน :)
R1S8K
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.