안녕하세요. 스마트 컨트렉트 개발자 개발이 체질의 최원혁입니다.
이번 게시글에서 Solidity에서 활용할 수 있는 비트마스크(BitMask) 연산에 대해 알아보겠습니다.
비트마스크(BitMask) 란?
10진수를 2진수로 표현하여 연산을 처리하는 기법입니다.
비트는 2진수(binary digit)를 뜻하는 말로 컴퓨터에서 사용되는 데이터의 최소 단위이며, 주로 Low-level 언어에서 사용하는 연산 기법입니다. 비트연산은 동작하는 메모리의 크기를 직접 설정할 수 있기에 메모리 사용량이 적습니다. 이는 Solidity 관점에서 더 적은 가스비를 사용하여 코딩을 할 수 있다는 걸 의미합니다. 또한 비트연산은 0(1)로 동작하여 수행속도가 아주 빠르고 코드가 짧습니다.
10진수와 2진수의 관계
비트마스크란 10진수를 2진수로 표현하여 연산을 처리하는 기법이라 설명드렸습니다. 때문에 비트마스크를 사용하기 위해서 컴퓨터가 어떻게 10진수를 2진수로 변환하는지 이해해야 합니다.
컴퓨터는 0(1)데이터를 통해 동작하게 되어 있습니다. 즉, 0(1)로된 언어만 읽고 이해할 수 있습니다.
하지만 0(1)로된 데이터는 사람이 알아보기 좋은 형태의 데이터가 아닙니다. 때문에 우리는 0(1) 형태의 컴퓨터 언어를 사람이 쉽게 알아볼 수 있도록 10진수로 변환하여 사용합니다.
Example
10진수는 위와 같은 비트(2진수)연산으로 계산되어 사람이 읽을 수 있는 형태로 변환됩니다.
1111 = 4bit = 2^3 + 2^2 + 2^1 + 2^0 = 15(4 bits Max Value)
1111 1111 = 8비트 = 2^7 + 2^6 + 2^5 + ... + 2^2 + 2^1 +2^0 = 255(8 bits Max Value)
0(1) 적힌 숫자 하나당 1비트(bit)의 용량을 가지며, 정해진 비트의 개수마다 할당되는 메모리 용량이 달라집니다.
4비트의 메모리를 할당받은 데이터는 최대 15 숫자까지 표현할 수 있고, 8비트를 할당받은 데이터는 최대 255 숫자까지 표현할 수 있습니다.
Solidity 비트마스크 연산자(Operators)
연산자 | 표기법 | 설명 |
and | & | 비트단위로 AND 연산을 한다. |
or | | | 비트단위로 OR 연산을 한다. |
xor | ^ | 비트단위로 XOR 연산을 한다. |
not | ~ | 단항 연산자로서 피연자의 모든 비트를 반전시킨다. |
shift left | << | 피연산자의 비트 열을 왼쪽으로 이동시킨다. |
shift right | >> | 피연산자의 비트 열을 오른쪽으로 이동시킨다. |
AND 연산 (&)
/*
* x = 1110 = 8 + 4 + 2 + 0 = 14
* y = 1011 = 8 + 0 + 2 + 1 = 11
* x & y = 1010 = 8 + 0 + 2 + 0 = 10
*/
function and(uint x, uint y) external pure returns(uint) {
return x & y; // 10
}
두 비트가 모두 1일때, 1을 반환합니다.
Example.
x = 1110 = 14
y = 1011 = 11
x & y = 1010 = 2^3 + 2^1 = 10
OR 연산 (|)
/*
* x = 1100 = 8 + 4 + 0 + 0 = 12
* y = 1001 = 8 + 0 + 0 + 1 = 9
* x | y = 1101 = 8 + 4 + 0 + 1 = 13
*/
function or(uint x, uint y) external pure returns(uint) {
return x | y; // 13
}
두 비트가 모두 1 또는 하나라도 0일 때, 1을 반환합니다.
Example.
x = 1100 = 12
y = 1001 = 9
x | y = 1101 = 2^3 + 2^2 + 2^0 = 13
XOR 연산 (^)
/*
* x = 1100 = 8 + 4 + 0 + 0 = 12
* y = 0101 = 0 + 4 + 0 + 1 = 5
* x ^ y = 1001 = 8 + 0 + 0 + 1 = 9
*/
function xor(uint x, uint y) external pure returns(uint) {
return x ^ y; // 9
}
두 비트가 서로 다를 때, 1을 반환합니다.
Example.
x = 1100 = 12
y = 0101 = 5
x ^ y = 1001 = 2^3 + 2^2 + 2^0 = 13
NOT 연산 (~)
/*
* x = 00001100 = 0 + 0 + 0 + 0 + 8 + 4 + 0 + 0 = 12
* ~x = 11110011 = 123 + 64 + 32 + 16 + 0 + 0 + 2 + 1 = 243
*/
function not(uint x) external pure returns(uint) {
return ~x; // 243
}
비트의 값을 반전하여 반환합니다.
Example.
x = 00001100 = 12
~x = 11110011 = 243
Shift Left 연산 (<<)
/*
* Example :
* 1 << 0 = 0001 --> 0001 = 1
* 1 << 1 = 0001 --> 0010 = 2
* 1 << 2 = 0001 --> 0100 = 4
* 1 << 3 = 0001 --> 1000 = 8
* 3 << 2 = 0011 --> 1100 = 12
*
* x = 0101 = 5
* bits = 2
* x << bits = 0101 --> 10100 = 16 + 0 + 4 + 0 + 0 = 20 // new position
*/
function shiftLeft(uint x,uint bits) external pure returns(uint) {
return x << bits; // 20
}
비트를 왼쪽으로 옮깁니다.
Example.
1 << 0 = 0001 --> 0001 = 1
1 << 1 = 0001 --> 0010 = 2
1 << 2 = 0001 --> 0100 = 4
1 << 3 = 0001 --> 1000 = 8
3 << 2 = 0011 --> 1100 = 12
기존의 비트 단위로 표현할 수 없는 경우, 새로운 메모리를 할당하여 표현합니다.
5 << 2 = 0101 --> 10100 = 20
Shift Rifht 연산 (>>)
/*
* Example :
* 8 >> 0 = 1000 --> 1000 = 8
* 8 >> 1 = 1000 --> 0100 = 4
* 8 >> 2 = 1000 --> 0010 = 2
* 8 >> 3 = 1000 --> 0001 = 1
* 8 >> 4 = 1000 --> 0000 = 0 // no Position, 0
*
* x = 0101 = 5
* bits = 2
* x >> bits = 0101 --> 0001 = 1
*/
function shiftRight(uint x,uint bits) external pure returns(uint) {
return x >> bits; // 1
}
비트를 오른쪽으로 옮깁니다.
Example.
8 >> 0 = 1000 --> 1000 = 8
8 >> 1 = 1000 --> 0100 = 4
8 >> 2 = 1000 --> 0010 = 2
8 >> 3 = 1000 --> 0001 = 1
기존의 비트 단위에서 벗어나는 경우, 0으로 표현합니다.
8 >> 4 = 1000 --> 0000 = 0
지금까지 Solidity 비트머스크 연산에 대해 알아봤습니다.
위 예시는 Soldity로 구현된 코드이며, 전체 Solidity코드는 아래 깃허브 링크를 첨부해두었습니다.
다음 시간에는 비트머스크 연산을 통해 가스비를 효율적으로 사용하는 스마트컨트렉트 알고리즘에 대해 배워보겠습니다.
📌 전체 코드(Github)
https://github.com/imelon2/My-Solidity-Playground/blob/main/Tool/BitMask.sol
댓글