정수에 대한 비트 연산자
Visual Basic, Visual Basic for Application, VBScript 등에서 사용하는 정수 연산자 및 이를 대상으로 수행되는 비트 연산자에 대해 정리해 본다.
정수형 데이터 타입
Visual Basic에서 미리 정의된 정수형 데이터 타입은 다음과 같다.
Byte
- 부호 없는 8비트 크기를 갖는다.
&H00
(0x00
) 이상&HFF
(0xFF
) 이하의 범위를 표현한다. Integer
- 부호 있는 16비트 크기를 갖는다. 즉 기본 정수형이 32비트 또는 64비트인 C-family 언어에 익숙해져 있다면 VB/VBA/VBScript에서는
Integer
가 16비트 크기를 갖고 있음을 항상 유념해 두어야 할 것 같다. Long
- 부호 있는 32비트 크기를 갖는다.
LongLong
- 부호 있는 64비트 크기를 갖는다.
LongPtr
- 시스템 의존형 정수 데이터 타입이다. 32비트로 구동되는 환경에서는
Long
과 같고, 64비트로 구동되는 환경에서는LongLong
과 같다. MSDN(https://docs.microsoft.com/en-us/office/vba/language/reference/user-interface-help/data-type-summary)에 따르면 이 자체는 독립된 자료형이 아니라 일종의 매크로인듯 하다.
비트 연산자
기본적으로 부정(NOT
), 비트 곱(AND
), 비트 합(OR
) 및 배타적 OR(XOR
)이 있는데 이건 단어 그대로가 연산자이다.
Dim x As Integer, y As Integer, z As Integer
' 이런 저런 코드들을 생략하고 ...
z = Not z ' z의 각 비트 반전을 z에 덮어써서 보관한다.
z = x And y ' x와 y의 비트 곱 결과를 z에 보관한다.
z = x Or y ' x와 y의 비트 합 결과를 z에 보관한다.
z = x Xor y ' x와 y의 배타적 OR 결과를 z에 보관한다.
이어서 살펴볼 것은 시프트(Shift
) 연산이다. 당황스럽게도 VB 계열의 언어에는 시프트 연산이 없다. 그러나 우회적으로 이와 비슷하게는 작성이 가능하다.
먼저 오버플로우 관련해서 발생할 수 있는 귀찮은 처리들을 방지하고자 현 데이터 타입보다 한 단계 더 큰 정수로 컨버팅을 시전한다.
Dim x As Integer
Dim t As Long
' 이런 저런 코드들을 생략하고 ...
t = CLng(x) ' Integer형 정수를 Long형 정수로 확장한다.
참고로 자매품인 CByte
(아무 값이나 Byte
로 형변환), CInt
(아무 값이나 Integer
로 형변환), CLngLng
(아무 값이나 LongLong
으로 형변환하는데 64비트 프로그램에서만 지원함)도 있으니 적절히 사용하면 될 것이다.
왼쪽으로 이동하는 시프트, 흔히 C-family에서는 <<
와 같은 모양으로 표현되는 연산자는 사실 2를 곱하는 것과 동일하다. 그래서 저렇게 한 단계 큰 타입으로 변환된 정수를 2로 곱하면 왼쪽 시프트를 한 번 수행한 것과 같게 된다. 반대로 오른쪽으로 이동하는 시프트, 흔히 C-family에서 >>
와 같은 모양으로 표현되는 연산자는 2로 나누는 것과 동일하다. 따라서 2로 나뉘주면 오른쪽 시프트를 한 번 수행한 것과 같게 된다.
Dim x As Integer
Dim t As Long
' 이런 저런 코드들을 생략하고 ...
t = CLng(x) ' Integer형 값을 Long형 값으로 확장하고
t = t * 2 ' t에 대해 왼쪽으로 한 번 시프트
t = t / 2 ' t에 대해 오른쪽으로 한 번 시프트
n번 시프트 하는 것은 2를 n번 곱하는 것과 같다. 이 때는 power 연산자(^
)를 쓰면 편리하다.
t = t * (2 ^ 3) ' t를 왼쪽으로 세 번 시프트한다 == t를 왼쪽으로 2의 3제곱만큼 곱한다.
t = t / (2 ^ 3) ' t를 오른쪽으로 세 번 시프트한다 == t를 오른쪽으로 2의 3제곱만큼 나눈다.
그 다음에 원래의 타입 크기에 맞추기 위해 자투리 비트들을 다듬은 다음...
t = t And &HFFFF ' Integer형은 16비트 크기를 갖는다고 했으므로 하위 16비트만 남기고 전부 버림
원래의 변수에 넣어주면 끝이다.
x = t
자바 같은 경우 좌측/우측 로테이션 연산이 있으나 이 역시 VB에는 없다. 물론 만들 수는 있으나 여백이 부족하여 자세한 설명은 생략한다.
텍스트 포매팅
C에서 scanf
같은 함수들을 사용하면 텍스트로 적힌 16진수, 10진수를 정수로 파싱이 가능하다. 마찬가지로 Classic VB에도 위에서 적은 CByte
따위의 컨버팅 함수를 쓰면 텍스트에서 정수로 파싱이 가능하다. 문제는 그 반대인 정수에서 텍스트로 서식 적용해서 출력하는 것이다. Classic VB에는 C의 printf
같은 기능이 없다. 따라서 텍스트 함수로써 약간의 편법을 써야 한다.
예를 들어 &H13A(0x13A
)라는 어떤 16진수를 32비트 형식에 맞추어 0000013A
와 같이 표현하고자 한다. 우선 Hex
함수는 자릿수 맞춤 없이 그냥 16진수로 출력한다.
Dim x as Integer
Dim o as String
x = &H13A
o = Hex(x)
이렇게 하면 o
라는 변수에는 13A라는 텍스트가 보관된다. 16진수임을 식별하는 어떤 접두어나 접미어도 없고 자릿수 맞춤도 없다. 이번에는 이렇게 해 보겠다.
o = "00000000" & Hex(x)
Hex
의 반환형은 문자열이니까 그 앞에 "00000000"을 붙인다. 그러면 o
의 결과는 "0000000013A"
처럼 된다. 32비트는 이 문자열에서 오른쪽부터 8문자만을 취하면 된다. 나머지는 어차피 영(0)이므로 버려도 무방하다. 이 때 사용되는 함수가 문자열 함수인 Right
이다.
o = "00000000" & Hex(x)
o = Right(o, 8) ' 오른쪽부터 8문자만 취한다.
한 줄로 표현하면,
o = Right("00000000" & Hex(x), 8)
이렇게 해서 정수를 16진수로 출력하되 32비트 자릿수에 맞추어 보기 좋게 출력할 수 있다.
필요하다면 여기에 적절한 접두어나 접미어를 적어주면 완벽하다.
o = "0x" & Right("00000000" & Hex(x), 8) ' C 스타일 0x0000013A
o = "&H" & Right("00000000" & Hex(x), 8) ' VB 스타일 &H0000013A
덧
상수 적을 때 Integer
범위를 넘어서는 값을 적으면 오버플로우 오류가 난다.
Dim x As Long
x = &HFFFFFF ' 오류
이 때는 값 끝에 &
기호를 붙여서 Long
형 임을 명시해주면 해결된다.
x = &HFFFFFF& ' Long형 값으로서 0x00FFFFFF
x = 50000& ' Long형 값으로서 50000