bit값을 뒤집는 좋은 방법 없나요?

thisrule의 이미지

한 바이트 data를 뒤집는 좋은 방법 없나요?
예를들어 a라는 한 바이트 변수에 이진수로 01010100 이 저장되어 있을때
0bit값은 7bit값으로, 1bit값은 6bit값으로... reverse하여
00101010으로 저장되게 하는 좋은 방법 있으면 알려주세요.

이걸 위해 for문을 돌리자니 보기에도 안좋고, 시간도 많이 걸리고, 멋도 없네요.
뭔가 좋은 알고리듬이 있을거 같은데...

File attachments: 
첨부파일 크기
PDF icon 00890370.pdf131.47 KB

댓글

sodomau의 이미지

( ((byte & 0x1) << 7) | ((byte & 0x2) << 5) | ((byte & 0x4) << 3) | \
((byte & 0x8) << 1) | ((byte & 0x10) >> 1 ) | ((byte & 0x20) >> 3) | \
((byte & 0x40) >> 5) | ((byte & 0x80) >> 7) )

외에 특별한 방법이 있을까요? 음;

dorado2의 이미지

IEEE 에서 bit reverse로 journal 검색해보세요.

선지자들의 알고리즘이 많이 있습니다. pseudo code까지 포함해서 말이지요.

댓글 첨부 파일: 
첨부파일 크기
PDF icon 0바이트
newmania의 이미지


const unsigned char BIT8[8] = {0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};

const unsigned char BIT8REV[8] = {0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01};

unsinged char ReverseByte(unsigned char byte)
{
unsigned char byte_rev=0x00;

for(i=0;i<8;i++)
{
if(byte&BIT8[i]) byte_rev |= BITREV[i]
}

return byte_rev;
}
/* 여기까지 */

이렇게 하면 되지 않을까요? ^^;

bugiii의 이미지

256 크기의 배열을 만드시고 인덱스와 원하는 거꾸로의 값으로 채운 다음 필요할 때 그 배열을 참조하시면 바로 답이 나오지 않을까요?

simpid의 이미지

저도 이 방법에 한표던집니다.

일반적인 상황에서 256바이트는 별 문제가 안되죠.

복잡하게 코딩해봐야 컴파일하면 256바이트 이상될지도 모르겠고

차라리 256 바이트 룩업테이블 만들고 배열 참조하는게 속도빠르고 좋을듯 합니다.

pynoos의 이미지

한번 만들어 보았습니다.

#include <stdio.h>

inline unsigned char left_rotate( unsigned char x, int l )
{
        return ((x<<l) & 0xff) | ((x>>(8-l)) & 0xff);
}

int main()
{
        unsigned char x,y;
        x = 0x54; /* expected: 0x2a */
        printf("%x\n", x );
        y = left_rotate( x,4 );
        x = (x & 0xcc) | (y & 0x33);
        y = left_rotate( x,1 );
        x = left_rotate( y,2 );
        y = (x & 0xaa) | (y & 0x55 );
        printf("%x\n", y );
        return 0;
}

좀 난해하죠?

토끼아빠의 이미지

간단하게 처리했네요...
아주 잘 됩니다.
그런데 이해가 잘 ~~~

좋은 하루 되세요!!

좋은 하루 되세요!!

thisrule의 이미지

pynoos wrote:
한번 만들어 보았습니다.
#include <stdio.h>

inline unsigned char left_rotate( unsigned char x, int l )
{
        return ((x<<l) & 0xff) | ((x>>(8-l)) & 0xff);
}

int main()
{
        unsigned char x,y;
        x = 0x54; /* expected: 0x2a */
        printf("%x\n", x );
        y = left_rotate( x,4 );
        x = (x & 0xcc) | (y & 0x33);
        y = left_rotate( x,1 );
        x = left_rotate( y,2 );
        y = (x & 0xaa) | (y & 0x55 );
        printf("%x\n", y );
        return 0;
}

좀 난해하죠?


가장 원하는 스타일입니다.
그대로 해보니 잘 동작합니다. 그런데 이해가 잘 안되는군요.
어떤 원리인가요?
우선 한 바이트의 nibble을 서로 자리 바꾼것 까지는 이해했습니다만, 그 다음이 통...?!?
cedar의 이미지

ANSI C++ 표준 라이브러리의 bitset 클래스와 reverse 알고리듬을 사용하면 간단하게 구현할 수 있습니다. (5분에 완성... ^^)

//---------------------------------------------------------------------------
#include <iostream>
#include <string>
#pragma hdrstop
#include <bitset>
#include <algorithm>
//---------------------------------------------------------------------------

// unsigned char 뿐만 아니라, unsigned short와 unsigned int에도 적용 가능한 템플릿 함수입니다.
template <typename T>
T reverse_bits(T n)
{
    using namespace std;

    typedef bitset<sizeof(T) * 8> bitset_t;
    bitset_t b(n);
    string s = b.to_string<char, char_traits<char>, allocator<char> >();
    reverse(s.begin(), s.end());

    return bitset_t(s).to_ulong();
}

int main()
{
    using namespace std;

    typedef bitset<sizeof(unsigned char) * 8> bitset8;

    unsigned char n = 0x54, r = 0;

    cout.setf(ios::hex, ios::basefield);
    cout.setf(ios::showbase);
    cout << "n == " << hex << static_cast<int>(n) << " == " << bitset8(n) << endl;
    cout << "Reversing all bits of m to r ...\n";
    r = reverse_bits(n);
    cout << "r == " << hex << static_cast<int>(r) << " == " << bitset8(r) << endl;

    return 0;
}
//---------------------------------------------------------------------------

실행 결과:

n == 0x54 == 01010100
Reversing all bits of m to r ...
r == 0x2a == 00101010
yui의 이미지

thisrule wrote:
pynoos wrote:
한번 만들어 보았습니다.
#include <stdio.h>

inline unsigned char left_rotate( unsigned char x, int l )
{
        return ((x<<l) & 0xff) | ((x>>(8-l)) & 0xff);
}

int main()
{
        unsigned char x,y;
        x = 0x54; /* expected: 0x2a */
        printf("%x\n", x );
        y = left_rotate( x,4 );
        x = (x & 0xcc) | (y & 0x33);
        y = left_rotate( x,1 );
        x = left_rotate( y,2 );
        y = (x & 0xaa) | (y & 0x55 );
        printf("%x\n", y );
        return 0;
}

좀 난해하죠?


가장 원하는 스타일입니다.
그대로 해보니 잘 동작합니다. 그런데 이해가 잘 안되는군요.
어떤 원리인가요?
우선 한 바이트의 nibble을 서로 자리 바꾼것 까지는 이해했습니다만, 그 다음이 통...?!?

가상적으로 87654321을 놓고 생각하면 편합니다.
bit도 아니고.. 암튼 자리수 채우는 물건입니다.

        x =  87654321
        y = left_rotate( x,4 );         // y = 43218765
        x = (x & 0xcc) | (y & 0x33);  // x = 87214365
        y = left_rotate( x,1 );        // y = 72143658
        x = left_rotate( y,2 );        // x= 14365872
                                        // 오라 x의 홀수번째는 1,3,5,7 y의 짝수번째는 2,4,6,8
        y = (x & 0xaa) | (y & 0x55 );    // 그것들만 가져와서 or하니 12345678!!

 
토끼아빠의 이미지

이제야 이해가 갑니다.
설명 고맙습니다.

좋은 하루 되세요!!

좋은 하루 되세요!!

doldori의 이미지

cedar wrote:
typedef bitset<sizeof(unsigned char) * 8> bitset8;


정의상 sizeof(unsigned char) == 1이고, 1바이트는 8비트가 아닐 수도 있으므로

typedef bitset<numeric_limits<unsigned char>::digts> bitset_t;

가 더 좋겠습니다.

cedar의 이미지

doldori wrote:
cedar wrote:
typedef bitset<sizeof(unsigned char) * 8> bitset8;


정의상 sizeof(unsigned char) == 1이고, 1바이트는 8비트가 아닐 수도 있으므로

typedef bitset<numeric_limits<unsigned char>::digts> bitset_t;

가 더 좋겠습니다.

좋은 지적 감사합니다. 수정해서 다시 올립니다.

//---------------------------------------------------------------------------
#include <iostream>
#include <limits>
#include <string>
#pragma hdrstop
#include <bitset>
#include <algorithm>
//---------------------------------------------------------------------------

template <typename T>
T reverse_bits(T n)
{
    using namespace std;

    typedef bitset<numeric_limits<T>::digits> bitset_t;
    bitset_t b(n);
    string s = b.to_string<char, char_traits<char>, allocator<char> >();
    reverse(s.begin(), s.end());

    return bitset_t(s).to_ulong();
}

int main()
{
    using namespace std;

    typedef bitset<numeric_limits<unsigned char>::digits> bitset_uc;

    unsigned char n = 0x54, r = 0;

    cout.setf(ios::hex, ios::basefield);
    cout.setf(ios::showbase);
    cout << "n == " << hex << static_cast<int>(n) << " == " << bitset_uc(n) << endl;
    cout << "Reversing all bits of m to r ...\n";
    r = reverse_bits(n);
    cout << "r == " << hex << static_cast<int>(r) << " == " << bitset_uc(r) << endl;

	return 0;
}
//---------------------------------------------------------------------------
pynoos의 이미지

thisrule wrote:
그대로 해보니 잘 동작합니다. 그런데 이해가 잘 안되는군요.
어떤 원리인가요?
우선 한 바이트의 nibble을 서로 자리 바꾼것 까지는 이해했습니다만, 그 다음이 통...?!?

yui 님께서 잘 설명해주셔서 더 설명할 것이 없군요.
rotate, shift, bit 연산이 어셈블리어일때 적당한 알고리즘입니다만,
sodomau 님이 하신것과 성능상 얼마나 좋은지는 모르겠습니다.

일단 가독성은 너무 떨어지고, 그렇다고 연산이 그렇게 작은 것 같지는 않거든요.

htonl 함수같은 구현을 물어 볼때 알고있는 알고리즘이라서 bit 수준에서도 적용해볼까해서 만들어 본 것입니다.

thisrule의 이미지

cedar wrote:
doldori wrote:
cedar wrote:
typedef bitset<sizeof(unsigned char) * 8> bitset8;


정의상 sizeof(unsigned char) == 1이고, 1바이트는 8비트가 아닐 수도 있으므로

typedef bitset<numeric_limits<unsigned char>::digts> bitset_t;

가 더 좋겠습니다.

좋은 지적 감사합니다. 수정해서 다시 올립니다.

//---------------------------------------------------------------------------
#include <iostream>
#include <string>
#pragma hdrstop
#include <bitset>
#include <algorithm>
//---------------------------------------------------------------------------

template <typename T>
T reverse_bits(T n)
{
    using namespace std;

    typedef bitset<numeric_limits<T>::digits> bitset_t;
    bitset_t b(n);
    string s = b.to_string<char, char_traits<char>, allocator<char> >();
    reverse(s.begin(), s.end());

    return bitset_t(s).to_ulong();
}

int main()
{
    using namespace std;

    typedef bitset<numeric_limits<unsigned char>::digits> bitset_uc;

    unsigned char n = 0x54, r = 0;

    cout.setf(ios::hex, ios::basefield);
    cout.setf(ios::showbase);
    cout << "n == " << hex << static_cast<int>(n) << " == " << bitset_uc(n) << endl;
    cout << "Reversing all bits of m to r ...\n";
    r = reverse_bits(n);
    cout << "r == " << hex << static_cast<int>(r) << " == " << bitset_uc(r) << endl;

	return 0;
}
//---------------------------------------------------------------------------

좋은 프로그램 감사합니다.
근데 전 에러가 나네요. 이게 뭔 조화인지...

Quote:
[ulssys@swdev5 reverse]$ g++ -Wall -o reverse reverse.cpp
reverse.cpp:3: warning: ignoring #pragma hdrstop
reverse.cpp: In function `T reverse_bits(T)':
reverse.cpp:14: parse error before `,' token
reverse.cpp:14: `char_traits<char>' specified as declarator-id
reverse.cpp:14: two or more data types in declaration of `char_traits<char>'
reverse.cpp:14: `allocator<char>' specified as declarator-id
reverse.cpp:14: two or more data types in declaration of `allocator<char>'
reverse.cpp:14: parse error before `>' token
reverse.cpp: In function `T reverse_bits(T) [with T = unsigned char]':
reverse.cpp:30: instantiated from here
reverse.cpp:14: warning: unused variable `int char_traits<char>'
reverse.cpp:14: warning: unused variable `int allocator<char>'
cedar의 이미지

thisrule wrote:

좋은 프로그램 감사합니다.
근데 전 에러가 나네요. 이게 뭔 조화인지...

제가 지금 g++를 테스트할 수가 없습니다. 죄송....
제가 최초로 올린 소스는 Borland C++Builder 6으로 컴파일 했고요,
MS Visual C++.NET 2003(7.1)에서 컴파일할 때는
#include <limits>
를 추가해야만 컴파일됩니다.
코드 수정해 놓았습니다.

헤더파일간의 포함 관계는 표준에 정의되어 있지 않기 때문에, 컴파일러에 따라 이런 차이가 생깁니다.

그리고 #pragma hdrstop은 precompiled header에서 제외하는 헤더 파일을 지정하는 것으로, 주로 윈도용 컴파일러에서 사용합니다.
주로, 사용자 정의 헤더 파일이나, 템플릿 라이브러리 헤더파일 앞에 지정하지요.
대응하는 gcc의 #pragma는 잘 모르겠네요. 위 메시지처럼 잘못 지정해도 warning만 날 뿐, 컴파일은 이상없습니다.

yielding의 이미지

Quote:

template <typename T>
T reverse_bits(T n)
{
typedef bitset<numeric_limits<T>::digits> bitset_t;
bitset_t b(n);
string s = b.template to_string< char, char_traits<char>, allocator<char> >();

reverse(s.begin(), s.end());
return bitset_t(s).to_ulong();
}

b.template to_string 으로 고치면 됩니다...

Life rushes on, we are distracted

최종호의 이미지

bugiii wrote:
256 크기의 배열을 만드시고 인덱스와 원하는 거꾸로의 값으로 채운 다음 필요할 때 그 배열을 참조하시면 바로 답이 나오지 않을까요?

bugiii 님 방법 한표!! 8)

cedar의 이미지

yielding wrote:
Quote:

template <typename T>
T reverse_bits(T n)
{
typedef bitset<numeric_limits<T>::digits> bitset_t;
bitset_t b(n);
string s = b.template to_string< char, char_traits<char>, allocator<char> >();

reverse(s.begin(), s.end());
return bitset_t(s).to_ulong();
}

b.template to_string 으로 고치면 됩니다...

이런 문법이 있었군요.
그럼 표준에서는 g++의 방식만 인정하는 건가요?
BC++과 VC++의 문법은 표준에 위배되는 건가요?

yielding의 이미지

흠 제가 지금 맥을 쓰고 있어서 vc 7.1에서는
테스트 할 수 없군요..

.tempalte 혹은 ->template construct가 고안된 이유가 위의 예제와 같은 문맥에서 컴파일러가 '<' 이 문자가 less then인지 아님 template의 시작을 의미하는 문자인지 알 수 있는 방법이 없기 때문임을 생각한다면 vc7.1이 저 코드를 에러없이 컴파일 했다는게 좀 의심스럽군요.

bc++는 표준에 매우 위배되는게 많고 vc는 7.1이 나오면서 template partial specialization 및 기타 6.0의 많은 문제를 해결했다고는 아직 template쪽의 표준을 완전하게는 구현하지 못한것 같습니다. (C++ Templates의 more CRTP 예제를 컴파일 못하더군요) 뭐 gcc도 마찬가지이긴 하지만...

여하튼 reverse_bits 이 함수 여러모로 공부 많이 되서 좋습니다. ^^;

Life rushes on, we are distracted

김성진의 이미지

논란의 여지가 있지만,
제가 위의 답변을 보고 느낀껀, bugiii 님의 답을 제외하고는
일종의 기술적 유희만을 너무 염두에 두신 것 같습니다

문제가 1바이트에 대한 비트의 조작(8비트 라고 합시다)이라고 정의를
한 것 치고는, 너무 장황한 답변만이 올라오는군요.

옛말에도 하나의 문제에 대해 하나의 간단한 답, 하나의 복잡한 답이 있으면,
간단한 것이 진리라고 햇습니다.

저는 bugiii님의 말씀대로 lookup table이 가장 타당한 답이 아닌가 생각되는데요.

비트의 변환을 위해서 왜 굳이 비트 연산이 필요한가요?

이 간단한 문제에 c++에다 namespace에 template이라니!!! 맙소사..

고도의 추상화, 극도의 구체화, 에디슨을 그리워하다.

geek43의 이미지

#include <iostream>
#include <bitset>

using namespace std;

int main()
{
bitset<8> num1 = 10;
bitset<8> num2 = 0xff;
string outPut;

num1 ^= num2;

outPut = num1.to_string();
cout << outPut.c_str() << endl;

return 0;
}

cppig1995의 이미지

10111110을 01000001로 바꾸는 게 아니라 01111101로 바꾸는 문제입니다.



절망으로 코딩하고 희망으로 디버깅하자.

Real programmers /* don't */ comment their code.
If it was hard to write, it should be /* hard to */ read.

cedar의 이미지

김성진 wrote:
논란의 여지가 있지만,
제가 위의 답변을 보고 느낀껀, bugiii 님의 답을 제외하고는
일종의 기술적 유희만을 너무 염두에 두신 것 같습니다

문제가 1바이트에 대한 비트의 조작(8비트 라고 합시다)이라고 정의를
한 것 치고는, 너무 장황한 답변만이 올라오는군요.

옛말에도 하나의 문제에 대해 하나의 간단한 답, 하나의 복잡한 답이 있으면,
간단한 것이 진리라고 햇습니다.

저는 bugiii님의 말씀대로 lookup table이 가장 타당한 답이 아닌가 생각되는데요.

비트의 변환을 위해서 왜 굳이 비트 연산이 필요한가요?

가장 간단한 것이 항상 진리는 아닙니다.
여기서는 단지 8비트만 뒤집는 경우이지만, 16, 32, 64비트라면 어떨까요.
비트가 늘어남에 따라 룩업 테이블의 크기는 기하급수적으로 늘어나 버립니다.
임베디드 시스템에서는 아예 구현이 불가능하죠. i8051이나 AVR 같은 8비트 시스템에서는 256바이트 배열을 여러번 쓰는 것도 꽤 부담스러운 일입니다. 그래서 위와 같은 복잡한 비트 연산이 필요할 경우도 있는 겁니다.

김성진 wrote:

이 간단한 문제에 c++에다 namespace에 template이라니!!! 맙소사..

std::bitset은 ANSI C++의 표준 라이브러리에 포함되어 있는 기능입니다.
어떤 언어에서든지 표준 라이브러리는 언어의 일부로서, 그 언어를 사용하는 프로그래머에게는 공통적인 어휘 역할을 하는 것입니다.
콘솔 출력을 하기 위해, C에서는 printf, C++에서는 cout <<, Java에서는 System.out.println을 쓰는 것처럼, C++에서의 비트 조작에 bitset을 쓰는 건 너무나 자연스러운 겁니다.
C++ 언어의 특징 때문에 bitset::to_string() 함수를 쓰는 것이 좀 귀찮아서 어렵게 보였을 뿐, 제가 쓴 코드는 상당히 간단한 겁니다.

  1. bitset의 내용을 string으로 옮긴다.
  2. reverse 알고리듬을 적용한다.
  3. 변환된 string을 다시 bitset으로 변환한다.
표준 라이브러리를 알고 있는 C++ 프로그래머에게는
룩업 테이블 방식만큼 간단한 겁니다.
게다가 N <= 32비트의 어떤 경우에도 적용할 수 있죠.[/]
cedar의 이미지

geek43 wrote:

outPut = num1.to_string();

여기서 컴파일 에러가 납니다.
bitset::to_string()의 용법은 그렇게 간단하지 않습니다.
표준 라이브러리 레퍼런스를 잘 읽어보세요.
hb_kim의 이미지

cedar wrote:
여기서는 단지 8비트만 뒤집는 경우이지만, 16, 32, 64비트라면 어떨까요.
비트가 늘어남에 따라 룩업 테이블의 크기는 기하급수적으로 늘어나 버립니다.
임베디드 시스템에서는 아예 구현이 불가능하죠. i8051이나 AVR 같은 8비트 시스템에서는 256바이트 배열을 여러번 쓰는 것도 꽤 부담스러운 일입니다. 그래서 위와 같은 복잡한 비트 연산이 필요할 경우도 있는 겁니다.

CPU 사양이 정 안되면 니블 단위로 끊어서 16 바이트크기의 테이블을 구성해서 몇번 반복하면 됩니다. 8 비트 CPU 아니라, 프로그래머블 시퀀서일지라도 할건 해야죠.

게다가 초소형 임베디드 시스템에서는 설령 C/C++ 을 쓰더라도 C 라이브러리까지 쓸수있는 경우는 드뭅니다.

yielding의 이미지

Quote:
논란의 여지가 있지만,
제가 위의 답변을 보고 느낀껀, bugiii 님의 답을 제외하고는
일종의 기술적 유희만을 너무 염두에 두신 것 같습니다

논란보다는 다양한 글을 올린 나머지 분들이 성의를 폄하하는거 같아 기분이 좀 나쁩니다.

Quote:
문제가 1바이트에 대한 비트의 조작(8비트 라고 합시다)이라고 정의를
한 것 치고는, 너무 장황한 답변만이 올라오는군요.

문제의 스펙이 1바이트에 대한 조작이 전부가 아니죠. for루프도 쓰지 않고 멋도 있었으면 좋겠다는 것입니다. (끝가지 읽어보면) for루프 이야기가 나온거 보면 thisrule님도 lookup table 생각해보셨을 겁니다.

Quote:
옛말에도 하나의 문제에 대해 하나의 간단한 답, 하나의 복잡한 답이 있으면, 간단한 것이 진리라고 햇습니다.

옛말 인용은 여기서는 좀 적절하지 않은거 같습니다. python만 옳고 perl은
잘못된 건가요?

Quote:
저는 bugiii님의 말씀대로 lookup table이 가장 타당한 답이 아닌가 생각되는데요.

bugiii님의 답도 훌륭하지만 문제의 정확한 스펙과 의도를 구현하신건 아닙니다. 그래서 thisrule님은 pynoos님의 답이 자신이 원하는 것이라고 댓글 다셨지요.

Quote:
비트의 변환을 위해서 왜 굳이 비트 연산이 필요한가요?

비트의 변환을 위해서 왜 비트 연산을 쓰면 안되는가요?

Quote:
c++에다 namespace에 template이라니!!!

문제를 내신분이 c로만 구현해달라고 요구하셨나요? 저는 내심 이 문제가
명예의 전당의 다른 문제처럼 여러 언어로 재미있게 글이 이어지기를 기다리고
있었습니다.

Quote:
이 간단한 문제에 ... 맙소사..

아무리 문제가 간단하다고 생각하시더라도 다른 사람들이 성의를 다해 댓글을 달때는 이런 표현은 삼가해주세요 이 글이 괜히 명예의 전당에 올라왔겠습니까?

Life rushes on, we are distracted

bugiii의 이미지

아웅... 너무 심각하게 받아들이지 않으셨으면 합니다. 그냥 이런 저런 방법으로 구현할 수 있겠구나 하고 넘어가시면 좋겠습니다. 그냥 재미삼아 여러가지 구현이 나올 수도 있구요. 그런 와중에 좋은 방법이 나올 수도 있지 않을까요?

하다못해 CPU 종속적인 비트 역전 어셈블리 명령어도 하나의 답글이 될 수도 있습니다. (아예 비트를 역순으로 바꿔주는 명령어가 존재하는 CPU도 있습니다. 8, 16, 32비트 다 되는 걸로 기억합니다.)

특정 비트 유무의 검사를 마스크 배열을 이용해서 해결하거나 이런식의 비트 변경은 임베디드에서 흔히 쓰이는 기법이라 아무 생각없이 말씀드린거니까요. 그럴 수도 있구나 해주시면 좋겠습니다. 하드웨어에서도 어드레스 디코딩 회로를 논리회로로 하지 않고 일반 롬 테이블을 이용해서 무식하게 하는 방법도 있습니다. (물론 배보다 배꼽이 더 큰 경우가 많지만요)

저도 잘못한 것이 그 배열에 어떻게 정확하게 변환값을 집어 넣는가는 빠트린 것입니다. 그런 것을 다른 분들이 올려주신 알고리즘으로 해결하시면 될 것이구요.

중요한 것은 정확한 변환 값을 얻는 것이 먼저라고 생각합니다. 그 후에 좀 더 빠른 변환 알고리즘이나 테이블에 채워서 사용해야 한다고 생각합니다.

김성진의 이미지

역시나 비난의 글이 쏟아지는군요..

조목조목 비판하시는 모습을 보니, 왠지 서글프네요.

본의를 알아 주셨으면 좋겠습니다.

저도 거의 십여년간 이 바닥에서 구르고 있는 사람입니다.

답변을 다신 분들의 의도를 몰라서 그런게 아닙니다.

over-engineering이라는 화두가 생각이 나서 올린 글입니다.

건승하시길.

김성진 드림

고도의 추상화, 극도의 구체화, 에디슨을 그리워하다.

ㅡ,.ㅡ;;의 이미지

sodomau wrote:
( ((byte & 0x1) << 7) | ((byte & 0x2) << 5) | ((byte & 0x4) << 3) | \
((byte & 0x8) << 1) | ((byte & 0x10) >> 1 ) | ((byte & 0x20) >> 3) | \
((byte & 0x40) >> 5) | ((byte & 0x80) >> 7) )

외에 특별한 방법이 있을까요? 음;

이방법이 가장보편적으로 생각할수 있는방법인것 같은데..


----------------------------------------------------------------------------

hb_kim의 이미지

bugiii wrote:
하다못해 CPU 종속적인 비트 역전 어셈블리 명령어도 하나의 답글이 될 수도 있습니다. (아예 비트를 역순으로 바꿔주는 명령어가 존재하는 CPU도 있습니다. 8, 16, 32비트 다 되는 걸로 기억합니다.)

Bit reverse 오퍼레이션을 데이터에도 적용할수 있는 CPU 도 있나요? 보통 DSP 에서는 FFT 할때 쓰라고 어드레싱 레지스터에 bit reverse 옵션을 줄수있는 경우가 대부분이었던것 같은 기억이 가물가물한데요... FFT 말고는 별로 쓸데가 없었던것으로 기억도 나구요.

ㅡ,.ㅡ;;의 이미지

typedif struct
{
	unsigned int b1 : 1;
	unsigned int b2 : 1;
	unsigned int b3 : 1;
	unsigned int b4 : 1;
	unsigned int b5 : 1;
	unsigned int b6 : 1;
	unsigned int b7 : 1;
	unsigned int b8 : 1;
} RBIT;

RBIT bits, rbits;

rbits.b1 = bits.b8;
rbits.b2 = bits.b7;
rbits.b3 = bits.b6;
rbits.b4 = bits.b5;
rbits.b5 = bits.b4;
rbits.b6 = bits.b3;
rbits.b7 = bits.b2;
rbits.b8 = bits.b1;


----------------------------------------------------------------------------

ㅡ,.ㅡ;;의 이미지


typedef struct {
unsigned char b1 : 1;
unsigned char b2 : 1;
unsigned char b3 : 1;
unsigned char b4 : 1;
unsigned char b5 : 1;
unsigned char b6 : 1;
unsigned char b7 : 1;
unsigned char b8 : 1;
} t_bit;

int rev( int c )
{
int ret = 0;
t_bit bit, b2;

memcpy( &bit, &c, 1 );
b2.b1=bit.b8;
b2.b2=bit.b7;
b2.b3=bit.b6;
b2.b4=bit.b5;
b2.b5=bit.b4;
b2.b6=bit.b3;
b2.b7=bit.b2;
b2.b8=bit.b1;
memcpy( &ret, &b2, 1 );

return ret;
}

----------------------------------------------------------------------------
C Library Development Project


----------------------------------------------------------------------------

thisrule의 이미지

제가 발의한 내용이 명예의 전당에 올라간 줄도 모르고 있었군요.(부끄 :oops: )
물론 모두가 답글을 다신 분들의 힘이지요.
아뭏든 이렇게 많은 방법과 알고리듬이 있는줄 몰랐습니다.
또, C++에 대하여 더 깊이 공부해야겠다는 생각도 듭니다.

혹시 C/C++ 말고 python이나 perl로 하면 더 멋진 코드가 나올까요?

atie의 이미지

인라인 어셈블러로 rol 이나 ror을 쓰는게 가장 간단할 것 같군요.

----
I paint objects as I think them, not as I see them.
atie's minipage

비행소년의 이미지

TI의 240x 시리즈 제품은 어드레스 모드중 비트 반전 어드레스를 지원 합니다.
다른 시리즈는 안 다뤄 봐서 보르겠네요.

hb_kim wrote:
bugiii 씀:
하다못해 CPU 종속적인 비트 역전 어셈블리 명령어도 하나의 답글이 될 수도 있습니다. (아예 비트를 역순으로 바꿔주는 명령어가 존재하는 CPU도 있습니다. 8, 16, 32비트 다 되는 걸로 기억합니다.)

Bit reverse 오퍼레이션을 데이터에도 적용할수 있는 CPU 도 있나요? 보통 DSP 에서는 FFT 할때 쓰라고 어드레싱 레지스터에 bit reverse 옵션을 줄수있는 경우가 대부분이었던것 같은 기억이 가물가물한데요... FFT 말고는 별로 쓸데가 없었던것으로 기억도 나구요.

높이 날다 떨어지면.
아푸다 ㅡ,.ㅡ

HeartBit의 이미지

bugiii wrote:
256 크기의 배열을 만드시고 인덱스와 원하는 거꾸로의 값으로 채운 다음 필요할 때 그 배열을 참조하시면 바로 답이 나오지 않을까요?

table driven :!:
가장 빠르고, 비용도 저렴한 방법으로 보입니다. :wink:

nohmad의 이미지

ruby 버전입니다.

class Integer
  def bitrev(zfill=8)
    ("%0#{zfill}b" % self).reverse.to_i(2)
  end
end

puts "%064b" % (-2**32)
puts "%064b" % (2**32-1)
puts "%064b" % (-2**32).bitrev(64)
puts "%064b" % (2**32-1).bitrev(64)
죠커의 이미지

HeartBit wrote:
bugiii wrote:
256 크기의 배열을 만드시고 인덱스와 원하는 거꾸로의 값으로 채운 다음 필요할 때 그 배열을 참조하시면 바로 답이 나오지 않을까요?

table driven :!:
가장 빠르고, 비용도 저렴한 방법으로 보입니다. :wink:

멋진 방법이죠. 예전에 sin에서 쓴 것을 보고 충격먹었던 적이 기억납니다. :-)

[덧붙임: 원래 발제와는 좀 의도가 벗어난 것같군요 :-) ]

jongwooh의 이미지

CN wrote:
HeartBit wrote:
bugiii wrote:
256 크기의 배열을 만드시고 인덱스와 원하는 거꾸로의 값으로 채운 다음 필요할 때 그 배열을 참조하시면 바로 답이 나오지 않을까요?

table driven :!:
가장 빠르고, 비용도 저렴한 방법으로 보입니다. :wink:

멋진 방법이죠. 예전에 sin에서 쓴 것을 보고 충격먹었던 적이 기억납니다. :-)

[덧붙임: 원래 발제와는 좀 의도가 벗어난 것같군요 :-) ]

이건 메모릴 너무 먹겠군요. 4비트에 대해 16개의 테이블로 바꿔치기 하는걸 두번 반복하는게 낫겠습니다.

you must know the power of dark side.

cppig1995의 이미지

ㅡ,.ㅡ;; wrote:
typedif struct
{

typedif 를 typedef 로 바꿔야 하지 않나요?

Real programmers /* don't */ comment their code.
If it was hard to write, it should be /* hard to */ read.

ㅡ,.ㅡ;;의 이미지

cppig1995 wrote:
ㅡ,.ㅡ;; wrote:
typedif struct
{

typedif 를 typedef 로 바꿔야 하지 않나요?


ㅋㅋ


----------------------------------------------------------------------------

너바나의 이미지

바이트일경우..

                x = (x >> 4) | (x << 4);
                x = (x >> 2 & 0x33) | (x << 2 & 0xcc);
                x = (x >> 1 & 0x55) | (x << 1 & 0xaa);

이렇게 쓰는군요..

새로운거 종종 배우게 되는군요..

kenos의 이미지

비트 반전이면

C나 C++경우 ~ 연산자 하나면 끝나는데.....

제가 문제를 잘 못 파악한건지..
뒤늦은 댓글이지만

superkkt의 이미지

12345678을 87654321로..

======================
BLOG : http://superkkt.com

======================
BLOG : http://superkkt.com

UntoldStory의 이미지

이 책에 비트 다루는 신기한 공식이 많이 포함되어 있습니다.
비트와 바이트를 뒤집기라는 장이 따로 있을 정도입니다.

x라는 8비트 정수를 뒤집기 위해서는 (절반을 중심으로 반대 위치로 옮기는것)

s = (x* 0x00020202) & 0x01044010
t = (x* 0x00080808) & 0x02088020
결과 = mod(s+t,4095)

이렇게 하면 된다고 하네요 이해는 안되지만 신기하지 않습니까 ^^;
이책을 사놓고 볼 시간이 없어서 안보고 있었는데 이렇게 쓸모가 있네요

jachin의 이미지

재밌는 글입니다. ^^

CPU 마다 다르겠지만, 제일 간단한 Shift Right 명령과 Underflow 플래그를 사용해서 8 번의 논리연산을 통해 답이 나오게 하는 경우도 있고, 곱셈기 성능이 좋으면 곱셈기를 이용하여, 정수 나눗셈을 위한 LUT가 있으면 나눗셈을 이용한 방법도 쓸 수 있고요... 최상의 성능을 내는 요인은 CPU 내의 ALU 회로와 그것을 고려한 컴파일러가 얼마나 훌륭한 결과물을 뽑아내느냐에 따라 다르겠지요. ^^

여러가지 알고리즘으로 구현을 하는 이유도 각 아키텍쳐마다, 혹은 수행해야 하는 목적 프로세스마다 다르게 적용하기 위해서입니다.

공학에서는 성능과 관계없이 가장 적합한 것을 얻는 것이 답이니까요. :)
====
( - -)a 이제는 학생으로 가장한 백수가 아닌 진짜 백수가 되어야겠다.

JN의 이미지

변수 하나의 값을 뒤집는 상황이 아니라, unsigned char *array 형의 배열의 원소 전체의 값을 뒤집는 상황에 대해서 조사해 보았습니다. 일반적으로 reverse 함수를 배열의 원소의 수만큼 반복해서 호출해 주어야겠죠. 32비트 CPU인 경우, 이걸 어떻게 좀 효율적으로 처리할 수 없을까 조사하는 중에 재미있는 사실을 알아냈습니다.

unsigned int reverse_8x4( unsigned int c )
{
    c = ((c & 0x55555555) &lt;&lt; 1) | ((c &gt;&gt; 1) & 0x55555555);
    c = ((c & 0x33333333) &lt;&lt; 2) | ((c &gt;&gt; 2) & 0x33333333);
    c = ((c & 0x0f0f0f0f) &lt;&lt; 4) | ((c &gt;&gt; 4) & 0x0f0f0f0f);
 
    return c;
}

함수를 호출할 때, 캐스팅을 사용해서 배열요소 4개를 함꺼번에 32bit 정수형으로 해서 넘겨주게 되면, 각각 8비트 단위로 비트가 뒤집히게 됩니다. 다른 배열요소들을 건드리지 않습니다. 이 알고리즘이 가지는 또다른 장점이겠군요. :-)

unsigned char reverse( unsigned char c ); 형으로 함수를 설계하고 0x55, 0x33 등을 쓰는 것보다 많이 효율적으로 사용할 수 있을 거라 생각합니다.

16비트 배열의 각각의 요소를 뒤집을 경우에는

unsigned int reverse_16x2( unsigned int c )
{
    c = ((c & 0x55555555) &lt;&lt; 1) | ((c &gt;&gt; 1) & 0x55555555);
    c = ((c & 0x33333333) &lt;&lt; 2) | ((c &gt;&gt; 2) & 0x33333333);
    c = ((c & 0x0f0f0f0f) &lt;&lt; 4) | ((c &gt;&gt; 4) & 0x0f0f0f0f);
    c = ((c & 0x00ff00ff) &lt;&lt; 8) | ((c &gt;&gt; 8) & 0x00ff00ff);
 
    return c;
}

이 되겠죠.
ilhsu의 이미지

#include <stdio.h>
 
void main()
{
	//input은 입력값, result는 비트를 뒤집은 값을 저장할 8bit 변수
	char input, result=0;
	int i;
 
	input = 50; //임의의 값을 입력으로...
 
	//비트를 뒤집어 저장하는 루프
	for(i=0;i<8;i++)
	{
		result<<=1;
		input & (1<<i) ? result+=1 : result;
	}
}

개인적으로 for문을 쓰는게 더 직관적이고 간단해 보이는데...

/***********************************
웃자! 몸만 건강하면 뭘 못해먹고 살겠냐!
************************************/

/***********************************
웃자! 몸만 건강하면 뭘 못해먹고 살겠냐!
************************************/

새콤달콤의 이미지

테이블을 이용하면 좀 멋있으려나?

int main()
{
char bitRevTable[16] = {0,8,4,12,2,10,6,14,1,9,5,13,3,11,7,15};

char a = 0x35; // 예제..
char b;

b = (bitRevTable[(a >> 4)&0x0f]) | (bitRevTable[a & 0x0f] << 4);

...

return 0;
}

익명 사용자의 이미지

테이블을 이용하면 좀 멋있으려나?

int main()
{
char bitRevTable[16] = {0,8,4,12,2,10,6,14,1,9,5,13,3,11,7,15};

char a = 0x35; // 예제..
char b;

b = (bitRevTable[(a >> 4)&0x0f]) | (bitRevTable[a & 0x0f] << 4);

...

return 0;
}

익명 사용자의 이미지

테이블을 이용하면 좀 멋있으려나?

int main()
{
char bitRevTable[16] = {0,8,4,12,2,10,6,14,1,9,5,13,3,11,7,15};

char a = 0x35; // 예제..
char b;

b = (bitRevTable[(a >> 4)&0x0f]) | (bitRevTable[a & 0x0f] << 4);

...

return 0;
}

magingax의 이미지

너무 단순하게 해결했나? 위의 분들은 화려한 초식이 난무한데..흠좀무

BYTE inverse(BYTE s)
{
   return
     ((s & 0x80) >> 7 |
      (s & 0x40) >> 5 |
      (s & 0x20) >> 3 |
      (s & 0x10) >> 1 |
      (s & 0x08) << 1 |
      (s & 0x04) << 3 |
      (s & 0x02) << 5 |
      (s & 0x01) << 7);
}

LISP 사용자모임
http://cafe.naver.com/lisper
방송기술 개발업체
http://playhouseinc.co.kr

shint의 이미지

좋네요

----------------------------------------------------------------------------
젊음'은 모든것을 가능하게 만든다.

매일 1억명이 사용하는 프로그램을 함께 만들어보고 싶습니다.
정규 근로 시간을 지키는. 야근 없는 회사와 거래합니다.

각 분야별. 좋은 책'이나 사이트' 블로그' 링크 소개 받습니다. shintx@naver.com

appler의 이미지

Small is beautiful..

최적화를 위한 최적의 소스 코드!!

아름다움을 추구하는건 간단함이 살아 있어야 한다가..

제 생각입니다..

재밌네요

댓글달기.ㅋ.ㅋ


laziness, impatience, hubris

不恥下問 - 진정으로 대화를 원하면 겸손하게 모르는 것은 모른다고 말하는 용기가 필요하다.

netskate의 이미지


이런 저런 방식이 많이 나와 있군요..

제가 선호하는 건 굉장히 성능에 critical 한 경우가 아니라면
초보자도 이해가 쉽도록 하는 게 좋다.... 는 ..

===================================================
Make it Simple, Easy, Compact !!!!

silasoni의 이미지

잘 배웠습니다 ^^

coathanger의 이미지

잘 배웠습니다. 아.. 근데 글 저장할 수 있는 다른 방법은 없나요..

jjjajh2의 이미지

1.1+2+3+6=2.
2.=ㄱ
12비트를 1비트로는 못 만들어도 ㄱ으로 치환 할수 있죠
자연로그(ln)이용 하시면 더 빠르고요~~!!
-------------------------------------------
식탁은 언제든지 열려 있다 와서 먹으세요~!

http://nametag.naver.com/04449cOadeMKdcAU

inhosens의 이미지

__asm__ __volatile__ (
    "movb r0, #0x88"          "\n\t"
    "andb r3, r0, %1, ror#1"  "\n\t"
    "andb r4, r0, %1, ror#2"  "\n\t"
    "orrb r3, r3, r4, ror#1"  "\n\t"
    "andb r4, r0, %1, ror#3"  "\n\t"
    "orrb r3, r3, r4, ror#2"  "\n\t"
    "andb r4, r0, %1, ror#4"  "\n\t"
    "orrb %0, r3, r4, ror#3"  "\n\t"
    :"=r"(result)
    :"r"(src)
    :"r0", "r3", "r4"
);

제대로 돌아가는 지는 모르겠지만 대충 이런 느낌으로 코딩하면 될 거 같습니다.

somebody의 이미지

룩업 테이블도 좋고, 직접 비트 대입(매크로 포함)도 좋습니다. 상황에 따라... 하지만 비트 연산에 STL의 과도한 이용은 좀 그런 것 같고, 첫 댓글과 바로 윗글 방식은 불필요한 시프트 연산이 너무 많은 것 같습니다. (그래봤자 요즘 CPU에서 차이는 개발의 적혈구겠지만... 새발의 피라고 할랬는데 왜... --a)

저라면 일반적인 경우...

BYTE FlipByte(BYTE byInput)
{
   BYTE byResult = 0x00;
 
   for (int i = 0; i < 8; i++)
   {
      if (byInput & 0x80)
         byResult |= 0x80;
 
      byInput << 1;
      byResult >> 1;
   }
   return byResult;
}

뭐 이런 식...

꼭 8비트가 아니라도 좋지요. 변수형이나 좀 바꿔주고(BYTE -> WORD), 루프 반복 횟수 (8 -> 16) 바꾸고, 마스크 값만 바꾸면 되니까 (0x80 -> 0x8000).

inhosens의 이미지


컴파일해서 인스트럭션을 세어보시면
어떤게 불필요한 연산이 많은지 알게 되실 겁니다.
말씀하신 대로 루프를 돌리면 루프에 필요한 인스트럭션을 빼고 적게 잡아도
30개가 훌쩍 넘어가겠군요.

solbatso의 이미지

이건 실제로는 안하는게 좋은데요

레지스터에 값을 전달하는 코드를 작성한뒤,

결과값을 레지스터에 값을 복사하기 직전에 씨피유 입출력 포트만 핀배열을 바꿔꽂아봅시다.

멋은 있겠습니다.

바람과 같이 빨라야합니다. 전기보다 빨라야 할지도 모릅니다.

snowall의 이미지

빛의 속도로 움직이면 되겠군요...-_-

--------------------------
피할 수 있을때 즐겨라!
http://snowall.tistory.com

피할 수 있을때 즐겨라! http://melotopia.net/b

antibug의 이미지

같은 아이디어로 방법만 다르게 해보죠.

시리얼 통신, 그러니까 UART 말고 SPI 같은 걸로
쓰는 놈은 MSB first로 쓰고, 받는 놈은 LSB first로
받으면 되겠네요.

--------------------------------------
재미없는 일은 하지 말자는 인간 쓰레기.
-.-;

--------------------------------------
재미없는 일은 하지 말자는 인간 쓰레기.
-.-;

파도의 이미지

.

--------Signature--------
시스니쳐 생각 중..

tinywolf의 이미지

그냥 학교 숙제하듯이 바르고 정직하게(응?) 하나 만들어 보았습니다.

#include <iostream>
using namespace std;
 
// 데이터 덩어리에서 특정 위치의 1비트 얻기
bool getBit(const void* pData, int nIdx)
{
	int nByteIdx = nIdx / 8;
	int nBitIdx = nIdx % 8;
	unsigned char* pBytes = (unsigned char*)pData;
	return ((pBytes[nByteIdx] & (1 << (7 - nBitIdx))) != 0);
}
 
// 데이터 덩어리의 특정 위치에 1비트 쓰기
void setBit(void* pData, int nIdx, bool bChk)
{
	int nByteIdx = nIdx / 8;
	int nBitIdx = nIdx % 8;
	unsigned char* pBytes = (unsigned char*)pData;
	if (bChk)
		pBytes[nByteIdx] |= (1 << (7 - nBitIdx));
	else
		pBytes[nByteIdx] &= ~(1 << (7 - nBitIdx));
}
 
int main(void)
{
	char num[] = { 1, 2, 4, 8, -1 };
 
	// 초기 입력값 출력
	for (int i = 0; i < sizeof(num) * 8; i++)
	{
		if (i % 8 == 0)			cout << " ";
		else if (i % 4 == 0)	cout << ":";
		cout << (getBit(&num, i) ? "1" : "0");
	}
	cout << endl;
 
	// 비트 변경 테스트
	setBit(&num, 3, true);
	setBit(&num, 4, true);
	setBit(&num, 8, true);
 
	// 결과값 출력
	for (int i = 0; i < sizeof(num) * 8; i++)
	{
		if (i % 8 == 0)			cout << " ";
		else if (i % 4 == 0)	cout << ":";
		cout << (getBit(&num, i) ? "1" : "0");
	}
	cout << endl;
 
	// 비트 뒤집기
	bool b1, b2;
	for (int i = 0; i < sizeof(num) * 8 / 2; i++)
	{
		b1 = getBit(&num, i);
		b2 = getBit(&num, sizeof(num) * 8 - i - 1);
		setBit(&num, i, b2);
		setBit(&num, sizeof(num) * 8 - i - 1, b1);
	}
 
	// 결과값 출력
	for (int i = 0; i < sizeof(num) * 8; i++)
	{
		if (i % 8 == 0)			cout << " ";
		else if (i % 4 == 0)	cout << ":";
		cout << (getBit(&num, i) ? "1" : "0");
	}
	cout << endl;
 
	return 0;
}

결과:
[~]
tinywolf $ g++ revbit.cpp 
 
[~]
tinywolf $ ./a.out 
 0000:0001 0000:0010 0000:0100 0000:1000 1111:1111
 0001:1001 1000:0010 0000:0100 0000:1000 1111:1111
 1111:1111 0001:0000 0010:0000 0100:0001 1001:1000

임의의 크기의 데이터열에서 특정 위치의 비트를 읽고 쓰는 함수를 만들고
for 루프 사용해서 뒤집습니다.

시간,효율 뭐 이런거 묻지도 따지지 않고 로직 그대로 알기 쉽게 만든 것이라고나 할까요..

ㅡ_ㅡ;

ironiris의 이미지

속도가 중요하고 정말 byte 만을 대상으로 한다면
아래처럼... 구현하면 되지 않을까요?
php 라면

$table = array("00000000"=>"00000000","00000001"=>"10000000",블라블라,"11111110"=>"01111111","11111111"=>"11111111");
echo $table["00000001"];

c 라면
#include <stdio.h>
unsigned char table[256]={0x00,0x80,블라블라,0x7F,0xFF};
 
int main(void){
    char a=0x01;
    printf("%x\n",table[a]);
}

5년만에 날로먹는 코드를 포스팅했네요. --;
쓰고 나니 bugiii 님께서 말씀해주신 내용이네요.
tack0829의 이미지

음.. 루프로 그냥 구현하는게 제일 직관적일거 같은데...

루프를 좀 적게 돌리는 방법을 찾고....

	/**
	 * 2진 비트의 나열 순서를 뒤집는다.
	 * 
	 * @param n
	 * 
	 * @return
	 */
	public int reverseBit(int n)
	{
		int enc		= 0xFFFFFFFF;
		int big		= (1<<31);			// 부호비트를 고려하지 않고 교체 한다.(32bit)
		int small	= 1;
		int tmp;
 
		while(big>small || big<0)
		{
			tmp = (n & (big ^ enc) & (small ^ enc));
 
			if((n & big) != 0)
				tmp |= small;
			if((n & small) != 0)
				tmp |= big;
 
			n = tmp;
 
			big>>>=1;
			small<<=1;
		}
		return n;
	}

JAVA 코드 입니다.

big>>>=1;
이부분만 수정 하시면 C코드로 사용하셔도 무방합니다.

32bit를 대상으로 만들었지만,
앞에 big 변수의 값을 조절하면 bit값을 변경해서 실행할 수 있습니다.

jokerol의 이미지

어떻게 지우는지 모르겠네요

it takes a day to make you yawn, brother

jokerol의 이미지

못지움

it takes a day to make you yawn, brother

jokerol의 이미지

ㅋ _ ㅋ, 어느 분이 올렸던 포스트를 제가 올려 봅니다.

그런데 질문자분은 왜 for 문을 쓰면 안되나요? for문을 안 쓰고는 되지 않을 듯 싶은뎅?
안되는건 아니지만 어짜피 수동으로 열거 해야 하지 않나요?

=========== 어느 분의 블로그? 에서 ========================
비트를 뒤집어 반대의 숫자 만들기
Posted 2007/04/01 20:13
동아리 후배들이 시스템 프로그래밍이라는 과목을 듣는데, 그 숙제 중에 어려운 문제가 있었는지 모두 끙끙대고 있었습니다. 지금은 고수 형님의 도움으로 모두들 숙제를 끝낸 상태이지만, 차근차근 생각해 보면 그다지 어려운 문제는 아닌지라, 집에서 심심해하다가 제가 한번 풀어 보았습니다.
음, 어떤 16진수 숫자를 입력받으면 그 숫자의 비트를 뒤집어 다른 숫자로 만드는 것이 숙제입니다. 어떤 숫자 123456AB를 입력받으면, 이 숫자를 BA654321로 만드는 게 아니고, 예를 들어 1011010가 그 숫자의 비트면 이걸 0101101로 바꾸는 것입니다. 그러면 전혀 다른 숫자가 나오겠지요. 예를 들어 123456AB일 경우는, D56A2C48이란 숫자가 나와야 합니다.

void main()
{
        int input, answer, temp;
        int compare = 1;
        int count = 0;
        printf("input your hexadecimal number : ");
        scanf("%x", &input);
        for ( count = 0 ; count < 31 ; count++)
        {
                temp = input & compare;
                answer = answer | temp;
                input = input >> 1;
                answer = answer << 1;
        }
        printf("%X\n", answer);
}

비트 연산자 중 &연산은, A와 B가 모두 1일 때만 1을 리턴하고 나머지의 경우는 모두 0을 리턴합니다. 또한 |연산의 경우는 A와 B중 한 개만 1이라도 1을 리
턴하는 연산자입니다. 따라서 이 두개를 이용해서 위의 compare라는 변수를 1로 잡고서 입력받은 숫자의 비트를 계속 비교하며 시프트해서 비트값을 반대로 채워넣으면 비트를 뒤집을 수가 있겠지요.

compare라는 변수의 값이 1이므로, 비트로 치면 00000000 00000000 00000000 00000001 이고
이걸 입력받은 숫자와 &연산을 했을때 나올 수 있는 숫자는 끝이 0이거나 끝이 1인 두 가지 경우밖에는 없습니다. 이걸 결과 변수와 |연산을 하면(or) 결과에 값을 채워넣을 수 있습니다. 값을 채우면, 입력받은 숫자는 오른쪽으로 시프트, 답을 저장하는 숫자는 왼쪽으로 시프트를 하고 또 다음 숫자와 비교를 합니다. 비트는 int가 4바이트, 즉 32비트이므로 32번 시프트를 하면 모든 비트의 비교가 끝이 나겠습니다.

화살표는 연산한 후에 시프트를 하라는 의미. in=input, com=compare, tem=temp, ans=answer.

it takes a day to make you yawn, brother

it takes a day to make you yawn, brother

jokerol의 이미지

글자 수 제한이?? 헐

it takes a day to make you yawn, brother

jokerol의 이미지

======아래는 위 포스트 보기 전 제가 필요해서 만들었던 것 ================

 for(i=0,j=0,k=0;i<=R_BIT_COUNT;i++)
   {
    k=(0x80>>i);
     // 이부분을 참조를 사용한 goto로 작성하면 더욱 빨라질 듯 (비교 없이 곧바로 점프,비트 연산 한번을 줄일수 있다)
    if( ch_a_state_backup&(1<<i))
    {
     j|=k;
    }
   }
/* j 가 결과물 */

제기억에 goto 문 레이블도 참조 테이블로 만들 수 있었던 것 같던데, 그걸 사용하면 위의 것이 for문을 사용한 것 중 가장 빠른
것이 아닐까 생각합니다. 제가 올린 블로그 쓰신 분도 연산 횟수는 제가 만든거와 같습니다. 그런데 그분께 더 장점이 있는게 비트수가
고정되어 있지 않고 제꺼는 고정(0x80)되어 있죠.
그런데 그분 쓰신거에는 비교없이 전부 비트연산을하므로 제가 한 비교를 goto 레이블 룩업으로 변환하여 합치면
가장 빠를 듯.

it takes a day to make you yawn, brother

it takes a day to make you yawn, brother

익명 사용자의 이미지

마이컴 환경에서 사용중입니다.

u32 BitReverse(u32 value, u32 Length)
{
u32 result = 0;

while(Length--) {
result |= (value & 0x01) << Length;
value >>= 1;
}

return result;
}

룩업 테이블은 메모리가 아까워서 못씁니다.

mirheekl의 이미지

http://stackoverflow.com/questions/746171/best-algorithm-for-bit-reversal-from-msb-lsb-to-lsb-msb-in-c

메모리가 적은 경우에 최적화된 알고리즘, 룩업테이블을 이용한 알고리즘, 32/64bit 환경에 최적화된 방법 등등이 모두 나와 있습니다. 한번 읽어보시면 좋을 것 같네요

--

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.