programing

C/C++에서 바이트의 비트 순서를 되돌리는 가장 간단한 방법은 무엇입니까?

copysource 2022. 8. 31. 23:37
반응형

C/C++에서 바이트의 비트 순서를 되돌리는 가장 간단한 방법은 무엇입니까?

비트 순서를 바이트 단위로 되돌리는 방법은 여러 가지가 있지만 개발자가 구현하기 가장 쉬운 방법은 무엇인지 궁금합니다.그리고 뒤바뀌는 건...

1110 -> 0111
0010 -> 0100

이것은 이 PHP 질문과 비슷하지만 중복되지는 않습니다.

이것은 이 C 질문과 비슷하지만 중복되지 않습니다.이 질문은 개발자가 구현하기 가장 쉬운 방법을 묻는 질문입니다.「최적의 알고리즘」은, 메모리와 CPU의 퍼포먼스에 관계하고 있습니다.

이 조작은 유효합니다.

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

먼저 왼쪽 4비트를 오른쪽 4비트로 바꿉니다.다음으로 인접한 모든 페어가 스왑되고 다음으로 인접한 모든 싱글비트가 교환됩니다.그 결과 순서가 반대로 됩니다.

조회 테이블은 가장 간단한 방법 중 하나여야 합니다.그러나 전체 조회 테이블은 필요하지 않습니다.

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

이것은 코드화 및 육안으로 확인하는 것이 매우 간단합니다.
궁극적으로는 전체 테이블보다 더 빠를 수 있습니다.비트 산술은 저렴하고 테이블은 캐시 라인에 쉽게 들어갑니다.

"가장 간단한 방법"이라는 의미에 따라 비트를 반전시키는 방법이 많이 있습니다.


회전에 의한 후진

논리적인 은 첫 비트에 입니다.(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. 에 각 합니다.while (byte_len--)

  2. 더 .(b & 1); 이합니다.;는 1로, r은 1로 설정합니다.|하면 됩니다.(r << 1)

  3. 으로 부호 .b >>=1변수 b의 오른쪽 끝에 있는 비트를 지웁니다. >> 은 b /= 고 1 b > = 1 / b / = 2 2에 2 as as as as as as as as as as as as as as as as as as as as as as as as as as as as


한 줄로 역방향

이 솔루션은 Programming Hacks 섹션의 Rich Schroppel에 기인합니다.

unsigned char reverse_bits3(unsigned char b)
{
    return (b * 0x0202020202ULL & 0x010884422010ULL) % 0x3ff;
}
  1. 곱셈 연산(b * 0x02020202)ULL)은 8비트 바이트 패턴의 5개의 복사본을 생성하여 64비트 값으로 팬아웃합니다.

  2. AND 연산(& 0x010884422010ULL)은 각 10비트 그룹에 대해 올바른(역방향) 위치에 있는 비트를 선택합니다.

  3. 곱셈 및 AND 연산을 함께 사용하면 원래 바이트에서 비트가 복사되어 각각 10비트 세트 중 하나에 표시됩니다.원래 바이트에서 반전된 비트의 위치는 10비트 세트 내의 상대 위치와 일치합니다.

  4. 모듈러스 나눗셈을 2^10 - 1로 하는 마지막 단계(% 0x3ff)는 64비트 값으로 10비트(위치 0-9, 10-19, 20-29...)의 각 세트를 병합하는 효과가 있습니다.이들은 겹치지 않기 때문에 계수 분할의 기초가 되는 추가 단계는 OR 연산과 같이 동작합니다.


분할 및 정복 솔루션

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가 무엇을 의미하는지 시각화하는 것은 어렵다고 생각합니다.

C++14 규격부터 포함되어 있는 GCC 확장판인 '0b'를 이용하지 않기 때문에 이 답변은 2010년 4월로 거슬러 올라간다.

정수 상수는 '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;
}

음음음음 n음음 n:>> 4가 있기 1바이트에 8비트가 있기 때문에 부호 없는 문자이기 때문에 나머지 절반을 취하려고 합니다.

이 솔루션을 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;
}

C++ 17만

바이트의 역치를 저장하는 테이블을 다음과 같이 사용할 수 있습니다.(i * 0x0202020202ULL & 0x010884422010ULL) % 0x3ff、 lamda ( lamda , ((((를를((((((((((((( )로 컴파일해야 합니다.g++ -std=c++1zC++17) 이후에만 동작하기 때문에 테이블 내의 값을 반환하면 그에 따라 반전된 비트가 제공됩니다.

#include <cstdint>
#include <array>

uint8_t reverse_bits(uint8_t n) {
        static constexpr array<uint8_t, 256> table{[]() constexpr{
                constexpr size_t SIZE = 256;
                array<uint8_t, SIZE> result{};

                for (size_t i = 0; i < SIZE; ++i)
                    result[i] = (i * 0x0202020202ULL & 0x010884422010ULL) % 0x3ff;
                return result;
        }()};

    return table[n];
}

main.cpp

위의 기능을 포함하여 직접 사용해 보십시오.

#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

1바이트를 말하는 경우 256바이트를 사용할 수 없는 경우를 제외하고 테이블룩업이 가장 좋습니다.

완전한 테이블 룩업솔루션을 게시한 사람이 없기 때문에, 제 것은 다음과 같습니다.

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

많은 솔루션에 대한 자세한 내용은 빈둥빈둥거리는 해킹을 참조하십시오.거기서부터의 카피 페이스트는, 확실히 간단하게 실장할 수 있습니다.=)

예를 들어 (32비트 CPU의 경우)

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

「심플한 실장」이라고 하는 것은, 시험이나 취업 면접에서 참조를 하지 않고 실시할 수 있는 것을 의미하고 있는 경우, 비트를 1개씩 역순으로 다른 변수로 카피하는 것이 가장 안전합니다(이미 다른 답변에 나타나 있습니다).

다음 솔루션은 여기서 본 다른 비트 조작 알고리즘보다 간단하다고 생각합니다.

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

휴대용은 아닐지 몰라도 어셈블리 언어를 사용합니다.
많은 어셈블리 언어에는 비트를 반송 플래그로 회전시키고 반송 플래그를 워드(또는 바이트)로 회전시키는 명령이 있습니다.

알고리즘은 다음과 같습니다.

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

편집:

옵션 비트 수를 사용하여 템플릿으로 변환

심플하고 고속입니다.

reverse rv 문자 리버스(char rv)

없는 tmp부호 없는 char tmp=0;
=if(rv&0x01) tmp = 0x80;
| = ;if(f5&0x02) tmp | = 0x40
=if(f2&0x04) tmp |= 0x20;
=if(f5&0x08) tmp |= 0x10;
=if(f2&0x10) tmp |= 0x08;
=if(f2&0x20) tmp |= 0x04;
=if(f2&0x40) tmp |= 0x02;
=if(rv&0x80) tmp |= 0x01;
return tmp;
}

이는 의 우수한 답변과 유사한 방법이지만 최적화, 최대 64비트 정수 지원 및 기타 작은 개선 사항을 포함합니다.

함수 C++ 를 하고 .reverse_bits()컴파일러가 함수에 전달될 수 있는 정수의 다양한 워드 크기를 최적화하도록 합니다.이 함수는 최대 64비트까지 8비트의 배수인 모든 워드크기에서 올바르게 동작해야 합니다.사용 중인 컴파일러가 64비트보다 긴 단어를 지원하는 경우 메서드는 쉽게 확장할 수 있습니다.

이것은 필요한 헤더를 가진 완전하고 컴파일 가능한 예입니다. 기능이 .to_binary_str()모든 것을 나타내는 다양한 워드사이즈의 콜과 함께 이진수의 std:: 문자열 표현을 작성한다.

코멘트나 빈 행을 삭제하면, 기능은 꽤 콤팩트하고 시각적으로 쾌적합니다.

여기 랩스택에서 시험해 볼 수 있습니다.

// this is the only header used by the reverse_bits() function
#include <type_traits>

// these headers are only used by demonstration code
#include <string>
#include <iostream>
#include <cstdint>


template<typename T>
T reverse_bits( T n ) {
    // we force the passed-in type to its unsigned equivalent, because C++ may
    // perform arithmetic right shift instead of logical right shift, depending
    // on the compiler implementation.
    typedef typename std::make_unsigned<T>::type unsigned_T;
    unsigned_T v = (unsigned_T)n;

    // swap every bit with its neighbor
    v = ((v & 0xAAAAAAAAAAAAAAAA) >> 1)  | ((v & 0x5555555555555555) << 1);

    // swap every pair of bits
    v = ((v & 0xCCCCCCCCCCCCCCCC) >> 2)  | ((v & 0x3333333333333333) << 2);

    // swap every nybble
    v = ((v & 0xF0F0F0F0F0F0F0F0) >> 4)  | ((v & 0x0F0F0F0F0F0F0F0F) << 4);
    // bail out if we've covered the word size already
    if( sizeof(T) == 1 ) return v;

    // swap every byte
    v = ((v & 0xFF00FF00FF00FF00) >> 8)  | ((v & 0x00FF00FF00FF00FF) << 8);
    if( sizeof(T) == 2 ) return v;

    // etc...
    v = ((v & 0xFFFF0000FFFF0000) >> 16) | ((v & 0x0000FFFF0000FFFF) << 16);
    if( sizeof(T) <= 4 ) return v;

    v = ((v & 0xFFFFFFFF00000000) >> 32) | ((v & 0x00000000FFFFFFFF) << 32);

    // explictly cast back to the original type just to be pedantic
    return (T)v;
}


template<typename T>
std::string to_binary_str( T n ) {
    const unsigned int bit_count = sizeof(T)*8;
    char s[bit_count+1];
    typedef typename std::make_unsigned<T>::type unsigned_T;
    unsigned_T v = (unsigned_T)n;
    for( int i = bit_count - 1; i >= 0; --i ) {
        if( v & 1 )
            s[i] = '1';
        else
            s[i] = '0';

        v >>= 1;
    }
    s[bit_count] = 0;  // string null terminator
    return s;
}


int main() {
    {
        char x = 0xBA;
        std::cout << to_binary_str( x ) << std::endl;

        char y = reverse_bits( x );
        std::cout << to_binary_str( y ) << std::endl;
    }
    {
        short x = 0xAB94;
        std::cout << to_binary_str( x ) << std::endl;

        short y = reverse_bits( x );
        std::cout << to_binary_str( y ) << std::endl;
    }
    {
        uint64_t x = 0xFEDCBA9876543210;
        std::cout << to_binary_str( x ) << std::endl;

        uint64_t y = reverse_bits( x );
        std::cout << to_binary_str( y ) << std::endl;
    }
    return 0;
}

컴파일러가 부호 없는길이를 허용한다고 가정하면:

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

여기서 발견됨

다음은 적합 할 수 쉬운 솔루션입니다. 플랫폼에는, 「」이 있는 됩니다.이러한 솔루션은, 이하의 플랫폼을 포함한, 모든 준거 플랫폼에 휴대할 수 있습니다.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;
}

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에 사용했습니다.라벨은 보통 상수이며 일반적으로 8진수입니다.

이 라벨을 빅 엔디언 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));

두 줄:

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"은 되돌리는 바이트입니다."displicate"는 0으로 초기화된 결과입니다.

소형 마이크로컨트롤러를사용하여설치면적이작은고속솔루션이필요한경우에는이솔루션이될수있습니다.C 프로젝트에 사용할 수 있지만 이 파일을 C 프로젝트에 어셈블러 파일 *.asm으로 추가해야 합니다.순서:C 프로젝트에서 다음 선언을 추가합니다.

extern uint8_t byte_mirror(uint8_t);

C에서 이 함수를 호출합니다.

byteOutput= byte_mirror(byteInput);

이것은 코드이며, 8051 코어에만 적합합니다.CPU 레지스터 r0은 byteInput으로부터의 데이터입니다.코드 우측 r0 크로스 캐리 회전 후 캐리 좌측을 r1로 회전합니다.비트마다 이 절차를 8회 반복합니다.그런 다음 레지스터 r1이 byteOutput으로 c 함수로 돌아갑니다.8051에서는 코어는 애큐뮬레이터 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 <-캐리

보다 느리지만 심플한 실장:

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

이것이 빠른 해결책이 될 수 있을까요?

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을 사용하는 번거로움에서 벗어납니다! 하지만 전문가들은 이것이 효율적이고 빠른지 알려 주시겠습니까?

도 관심을 것 같습니다.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하려면 ""로) c++0x 확장자가 합니다.{...}bitset ★★★std::vector<bool> a)boost::dynamic_bitset되지 않고 비트를 시킬 수 것을 는 바이트 또는 워드에 한정되지 않고 임의의 수의 비트를 반전시킬 수 있다는 것을 의미합니다.

HTH

이 단순 기능은 마스크를 사용하여 입력 바이트의 각 비트를 테스트하고 이를 시프트 출력으로 전송합니다.

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

이것은 제공된 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 ) 

가장 간단한 방법은 아마도 루프 내의 비트 위치 상에서 반복하는 것입니다.

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

테이블 룩업 또는

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

편집하다

귀사에 적합한 다른 솔루션을 찾아보십시오.

알고리즘 솔루션을 실장하기 전에 사용하고 있는 CPU 아키텍처의 어셈블리 언어를 확인합니다.아키텍처에는 다음과 같은 비트 조작을 처리하는 명령이 포함될 수 있습니다(단일 어셈블리 명령보다 간단한 것은 무엇입니까?).

만약 그런 지시가 없다면 룩업 테이블 루트로 가는 것이 좋습니다.테이블을 생성하기 위한 스크립트/프로그램을 작성할 수 있습니다.검색 조작은 여기의 비트 반전 알고리즘보다 빠릅니다(검색 테이블을 어딘가에 저장해야 합니다).

이것은 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;
}

이 질문이 오래되었다는 것은 알지만, 저는 여전히 그 주제가 어떤 목적과 관련이 있다고 생각합니다. 그리고 여기 매우 잘 작동하고 읽을 수 있는 버전이 있습니다.그것이 가장 빠르거나 가장 효율적이라고는 말할 수 없지만, 가장 깨끗한 것 중 하나일 것입니다.비트 패턴을 쉽게 표시할 수 있는 도우미 기능도 추가했습니다.이 함수는 자체 비트 조작기를 쓰는 대신 일부 표준 라이브러리 함수를 사용합니다.

#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

이것은 오래된 질문이지만, 아무도 명확하게 쉬운 방법을 보여주지 않은 것 같습니다(가장 가까운 것은 edW).이것을 테스트하기 위해서 C#을 사용했습니다만, 이 예에서는 C로 간단하게 할 수 없는 것은 없습니다.

void PrintBinary(string prompt, int num, int pad = 8)
{
    Debug.WriteLine($"{prompt}: {Convert.ToString(num, 2).PadLeft(pad, '0')}");
}

int ReverseBits(int num)
{
    int result = 0;
    int saveBits = num;
    for (int i = 1; i <= 8; i++)
    {
        // Move the result one bit to the left
        result = result << 1;

        //PrintBinary("saveBits", saveBits);

        // Extract the right-most bit
        var nextBit = saveBits & 1;

        //PrintBinary("nextBit", nextBit, 1);

        // Add our extracted bit to the result
        result = result | nextBit;

        //PrintBinary("result", result);

        // We're done with that bit, rotate it off the right
        saveBits = saveBits >> 1;

        //Debug.WriteLine("");
    }

    return result;
}

void PrintTest(int nextNumber)
{
    var result = ReverseBits(nextNumber);

    Debug.WriteLine("---------------------------------------");
    PrintBinary("Original", nextNumber);
    PrintBinary("Reverse", result);
}

void Main()
{
    // Calculate the reverse for each number between 1 and 255
    for (int x = 250; x < 256; x++)
        PrintTest(x);
}

이건 어때...

int value = 0xFACE;

value = ((0xFF & value << 8) | (val >> 8);
#include <stdio.h>
#include <stdlib.h>

#define BIT0 (0x01)
#define BIT1 (0x02)
#define BIT2 (0x04)
#define BIT3 (0x08)
#define BIT4 (0x10)
#define BIT5 (0x20)
#define BIT6 (0x40)
#define BIT7 (0x80)

#define BYTE_TO_BINARY_PATTERN "%c%c%c%c%c%c%c%c\n"

#define BITETOBINARY(byte) \
(byte & BIT7 ? '1' : '0'), \
(byte & BIT6 ? '1' : '0'), \
(byte & BIT5 ? '1' : '0'), \
(byte & BIT4 ? '1' : '0'), \
(byte & BIT3 ? '1' : '0'), \
(byte & BIT2 ? '1' : '0'), \
(byte & BIT1 ? '1' : '0'), \
(byte & BIT0 ? '1' : '0') \

#define BITETOBINARYREVERSE(byte) \
(byte & BIT0 ? '1' : '0'), \
(byte & BIT1 ? '1' : '0'), \
(byte & BIT2 ? '1' : '0'), \
(byte & BIT3 ? '1' : '0'), \
(byte & BIT4 ? '1' : '0'), \
(byte & BIT5 ? '1' : '0'), \
(byte & BIT6 ? '1' : '0'), \
(byte & BIT7 ? '1' : '0') \



int main()
{

    int i,j,c;

    i |= BIT2|BIT7;

    printf("0x%02X\n",i);    

    printf(BYTE_TO_BINARY_PATTERN,BITETOBINARY(i));

    printf("Reverse");

    printf(BYTE_TO_BINARY_PATTERN,BITETOBINARYREVERSE(i));

   return 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 레지스터에 있습니다.

언급URL : https://stackoverflow.com/questions/2602823/in-c-c-whats-the-simplest-way-to-reverse-the-order-of-bits-in-a-byte

반응형