Bit 자료형

김펭귄·2025년 7월 21일

C++

목록 보기
8/20

Bit 자료형 사용하는 이유

  • bool자료형의 경우 1byte의 크기를 가지지만, 사실 true, false 2개의 값만을 가지기에 1bit만이 필요함
  • 따라서 여러 bool변수를 한 번에 모아서 사용하도록 하기 위해 bit 변수를 사용
#include <bitset> // for std::bitset

std::bitset<8> bits {}; // 8 bit짜리
bits = 0b0000'0101; // 초기화
  • 가장 오른쪽이 0번째 bit이고 오른쪽에서 왼쪽으로 순서를 읽는다
  • 0b0000'0101의 경우 0, 2번째에 해당하는 bool 값이 1로 설정되어 있다

Bit 자료형

uintN_t

  • 비트 크기에 맞춰 uint8_t, uint16_t, uint32_t 를 사용
#include <cstdint>	// std::uintN_t

constexpr std::uint8_t bits8 { 0b0100'1011 };
constexpr std::uint16_t bits16 { 0x5B9D };
constexpr std::uint32_t bits32 { 0x001F'5B9D };

std::cout << bits8 << ',' << sizeof(bits8);	// K,1
std::cout << bits32 << ',' << sizeof(bits32);	// 2055069,4
  • 8, 16, 32 bit의 메모리만 사용하여 메모리 효율이 아주 좋다

  • 그러나 bit로 데이터를 해석하지 않음

    • uint8_t : C++에선 uint8_t가 실제로는 unsigned char로 정의되어 있음. 따라서 0b0100'101175로 저장되어 K를 출력
    • uint16_t : unsigned short 또는 unsigned short int로 정의됨. 정수형으로 취급된다
    • uint32_t : unsigned int로 정의되어 기본 정수형으로 취급됨
  • 따라서 캐스팅을 통해 원하는 자료형으로 출력하거나 사용해야 함

std::cout << static_cast<int>(bits8);	// 정수로 변환. 75
std::cout << std::bitset<8>(bits8); // bit로 변환. 01001011

bitset

#include <bitset>	// std::bitset

constexpr std::bitset<8> bitset8 { 0b1001'0001 };	// 145
constexpr std::bitset<8> bitset8 { 145 };	// 위와 동일
constexpr std::bitset<16> bitset16 { 0x4561 };
std::cout << bitset8;			// 10010001
std::cout << sizeof(bitset8);	// 4
  • bit 데이터를 관리하는 객체로, 실제 비트의 크기보다 더 큰 메모리를 차지
  • 대신, bit 관련 계산이나 함수 사용에 용이하다 (성능이 아주 좋음)
  • 데이터 해석은 bit 그 자체

bitset 활용

// 이진수 문자열로 생성
std::bitset<8> bits(std::string("11001100"));	// 이진수 문자열만 가능

// bitset → 이진수 문자열
std::string binaryStr = bits.to_string(); // "11001100"

// bitset → 정수(unsigned long)
unsigned long num = bits.to_ulong(); // 204
  • test() : 1인지 아닌지를 알려줌
  • set() : 1로 설정
  • reset() : 0으로 초기화
  • flip() : 값 변환
constexpr int  isHungry   { 0 };
constexpr int  isSad      { 1 };
constexpr int  isMad      { 2 };
constexpr int  isHappy    { 3 };
constexpr int  isLaughing { 4 };
constexpr int  isAsleep   { 5 };
constexpr int  isDead     { 6 };
constexpr int  isCrying   { 7 };

std::bitset<8> me{ 0b0000'0000 }; 
me.set(isHappy);      // set bit position 3 to 1 (0000 1000)
me.set(isHungry);     // set bit position 0 to 1 (0000 1001)
me.set(isCrying);     // set bit position 7 to 1 (1000 1001)

me.flip(isLaughing);  // flip bit 4 			 (1001 1001)
me.reset(isLaughing); // set bit 4 back to 0 	 (1000 1001)

std::cout << me;	// 1000 1001 반환
std::cout << "I am happy: " << me.test(isHappy) << '\n';	// 1반환
std::cout << "I am laughing: " << me.test(isLaughing) << '\n';	// 0반환
  • size() : bitset의 크기
  • count() : 1의 개수
  • all() : 모든 bit가 1로 켜져있는지를 반환
  • any() : 하나라도 1인지를 반환
  • none() : 1이 하나도 없는지를 반환
std::bitset<8> bits{ 0b0000'1101 };
std::cout << bits.size();		// 8
std::cout << bits.count();		// 3

std::cout << std::boolalpha;
std::cout << bits.all();		// false
std::cout << bits.any();		// true
std::cout << bits.none();		// false

비트 연산자

비트 할당 연산자

주의할 점

bitset 연산의 주의점

  • bitset은 bit data를 관리하기 위한 클래스 타입으로, 비트 연산자가 bitset끼리만 동작하도록 연산자 오버로딩이 되어 있음
	constexpr std::bitset<8> bitset_bits{ 0b0010'0100 };
	constexpr std::uint8_t uint_bits{0b1001'0001};

	std::cout << bitset_bits & uint_bits;	// error : bitset끼리만 연산해야함
  • 따라서 동일한 타입으로 변환하여 사용해주어야 한다
    uint8_t ub = 0b1010'1010;
    std::bitset<8> bs("11110000");

    // bitset을 unsigned long으로 변환 후 계산
    uint8_t u_bs = static_cast<uint8_t>(bs.to_ulong());
    auto result = ub & u_bs;		// result: 0b10100000 (int)

    // uint를 bitset으로 변환 후 계산
    std::bitset<8> bs_ub(ub);
    auto result_bs = bs_ub & bs;	// result: 0b10100000 (bitset)

uint_t 연산의 주의점

  • int보다 작은 uint8_t, uint16_t, uint32_t들은 비트 연산시에 int로 승격(변환)되어 연산됨
  • 따라서 값 손실, 예기치 못한 결과 발생
  • bitset은 이러한 type 승격 없이 연산이 이루어짐
#include <cstdint>
#include <bitset>

// uint8_t에 비트 연산시키면 결과는 int 타입
uint8_t v = 0b00001111;
auto res = ~v; // res는 int 타입, 0xFFFFFFF0

// bitset로 비트 연산하면 결과는 bitset<8> 타입 그대로
std::bitset<8> b1("00001111");
auto res2 = ~b1; // res2 역시 bitset<8> 타입, 11110000
std::uint8_t c { 0b00001111 };  // int보다 작으므로 int로 변환되어 연산

std::cout << std::bitset<32>(~c);     // 11111111111111111111111111110000
std::cout << std::bitset<32>(c << 6)  // 00000000000000000000001111000000
std::uint8_t cneg { ~c };             // error
c = ~c;                               // possible warning
  • 0b00001111int형인 0000'0000'0000'0000'0000'0000'0000'1111으로 변환되어 비트 연산 진행됨
  • 따라서 ~의 경우 앞에 28개의 1이 생성
  • <<의 경우에도 0b1100'0000에서 32bit로의 변환을 기대했으나 애초에 int로 계산되므로 그렇지 않은 결과가 나옴
  • cneg의 경우 리스트 초기화를 하였으므로, intuint8_t인 다른 자료형으로 초기화하려니 에러발생
  • c = ~c;은 경고만 발생 (narrowing : int -> uint8_t)

해결법

  • static_cast를 통해 계산 값을 원래 자료형으로 돌리고 후에 다시 원하는대로 변환
 std::uint8_t c { 0b00001111 };

// 00000000000000000000000011110000
std::cout << std::bitset<32>(static_cast<std::uint8_t>(~c)); 
// 00000000000000000000000011000000
std::cout << std::bitset<32>(static_cast<std::uint8_t>(c << 6));

std::uint8_t cneg { static_cast<std::uint8_t>(~c) };     
c = static_cast<std::uint8_t>(~c);              
  • 메모리 효율성보다 bitset을 이용한 계산 성능이 훨씬 뛰어나므로, 대부분의 bit data에는 bitset 사용하는 것이 더 좋다
  • uintN_t는 메모리 효율성이 중요하고, 계산이 거의 없는 단순 데이터 저장용으로 좋다

Bit Mask

  • 테이프를 붙여 페인트칠 하듯이, Bit Mask를 통해 원하는 bit만 수정
// 여러 형태로 bit mask 설정 가능(8 bitset 기준)
constexpr std::uint8_t mask0{ 0b0000'0001 }; 
constexpr std::uint8_t mask1{ 0b0000'0010 }; 
constexpr std::uint8_t mask2{ 0b0000'0100 }; 
constexpr std::uint8_t mask3{ 1 << 3 }; // 0000 1000
constexpr std::uint8_t mask4{ 1 << 4 }; // 0001 0000
constexpr std::uint8_t mask5{ 0x20 }; // hex for 0010 0000
constexpr std::uint8_t mask6{ 0x40 }; // hex for 0100 0000
constexpr std::uint8_t mask7{ 0b1000'0000 }; 

std::uint8_t myBits{ 0b0000'0101 }; 

Bit Mask Operation

  • bit 값 확인 : &
  • bit 1로 설정 : |
  • bit 값 초기화 : ~, & 사용
  • bit 값 flip : ^ 사용
std::uint8_t myBits{ 0b0000'0101 }; 

// &로 bit 값 확인
std::cout << (static_cast<bool>(flags & mask0);	// 1
std::cout << (static_cast<bool>(flags & mask1);	// 0

// |로 bit turn on
flags |= mask1; 								// 0000'0111
flags |= (mask4 | mask5); 						// 0011'0111 (동시에)

// bit 값 초기화
flags &= ~mask2; 								// 0011'0011
flags &= ~(mask1 | mask5); 						// 0001'0001 (동시에)

// bit flip
flags ^= mask2; 								// 0001'0101
flags ^= (mask4 | mask5); 						// 0010'0101 (동시에)

  • bitset의 함수 이용하는 것도 가능하나, Bit mask를 사용하면 한 번에 여러 bit를 관리할 수 있다

BitMask 예시

  • bitset을 이용한 예시
  constexpr std::bitset<4> isHungry   { 0b0001 };
  constexpr std::bitset<4> isSad      { 1 << 1 };
  constexpr std::bitset<4> isMad      { 4 };
  constexpr std::bitset<4> isHappy    { "1000" };

  std::bitset<4> me{}; 
  me |= (isHungry | isHappy); // I am hungry and happy
  me &= ~isHungry; // I am no longer hungry

  // bitset이므로 any()로 state 확인
  std::cout << std::boolalpha; 
  std::cout << "I am happy? " << (me & isHappy).any();
  std::cout << "I am sad? " << (me & isSad).any();
  std::cout << "I am hungry or sad? " << (me & (isHungry | isSad)).any();
  • uint_t를 이용한 예시
  constexpr std::uint8_t isHungry   { 0b0001 };
  constexpr std::uint8_t isSad      { 1 << 1 };
  constexpr std::uint8_t isMad      { 0b0100 };
  constexpr std::uint8_t isHappy    { 0b1000 };

  std::uint8_t me{}; 
  me |= (isHungry | isHappy); // I am hungry and happy
  me &= ~isHungry; // I am no longer hungry

  // uint이므로 직접 state 확인
  std::cout << std::boolalpha; 
  std::cout << "I am happy? " << static_cast<bool>(me & isHappy);
  std::cout << "I am laughing? " << static_cast<bool>(me & isLaughing);

Bit 예시

함수 매개인자

void someFunction(bool option1, bool option2, bool option3, bool option4, 
	bool option5, bool option6, bool option7, bool option8, bool option9, 
	bool option10, bool option11, bool option12, bool option13, bool option14, 
	bool option15, bool option16, bool option17, bool option18, bool option19, 
	bool option20, bool option21, bool option22, bool option23, bool option24, 
	bool option25, bool option26, bool option27, bool option28, bool option29, 
	bool option30, bool option31, bool option32);
  • bool형 매개인자를 일일이 다 받기보단, bit형으로 한 번에 관리
void someFunction(std::bitset<32> options);

someFunction(option10 | option32);

#define GL_DEPTH_BUFFER_BIT               0x00000100
#define GL_COLOR_BUFFER_BIT               0x00004000
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // OpenGL

RGBA data

  • RGBA의 각 data는 8bit로 총 32bit를 차지
  • 32bit 중 앞의 8bit씩 RGBA의 값을 가짐
	// Bit mask
	constexpr std::uint32_t redBits{ 0xFF000000 };
	constexpr std::uint32_t greenBits{ 0x00FF0000 };
	constexpr std::uint32_t blueBits{ 0x0000FF00 };
	constexpr std::uint32_t alphaBits{ 0x000000FF };

	std::cout << "Enter a 32-bit RGBA color value in hexadecimal (FF7F3300): ";
	std::uint32_t pixel{};
	std::cin >> std::hex >> pixel; // hex 값으로 받음

	// Bit Mask와 비트 연산자를 통해 각 RGBA의 값 추출
	std::uint8_t red{ static_cast<std::uint8_t>((pixel & redBits) >> 24) };
	std::uint8_t green{ static_cast<std::uint8_t>((pixel & greenBits) >> 16) };
	std::uint8_t blue{ static_cast<std::uint8_t>((pixel & blueBits) >> 8) };
	std::uint8_t alpha{ static_cast<std::uint8_t>(pixel & alphaBits) };

	std::cout << "Your color contains:\n";
	std::cout << std::hex; // hex 값으로 출력

	// uint8_t는 출력시 문자로 출력하므로, int로 변환 후 출력
	std::cout << static_cast<int>(red)   << " red\n";
	std::cout << static_cast<int>(green) << " green\n";
	std::cout << static_cast<int>(blue)  << " blue\n";
	std::cout << static_cast<int>(alpha) << " alpha\n";

Enter a 32-bit RGBA color value in hexadecimal (e.g. FF7F3300): FF7F3300
Your color contains:
ff red
7f green
33 blue
0 alpha

Reference

learn.cpp

profile
반갑습니다

0개의 댓글