숫자 타입
숫자 타입
Go에서 정수라거나 소수, 복소수를 담고 싶을 때, 그냥 아무 숫자 타입이나 고르면 될까요? 아쉽게도 아닌데요. 같은 "숫자"라도 담을 수 있는 값의 범위가 서로 다르고, 심지어 아키텍처에 따라 크기가 바뀌는 타입도 있어요. 여기선 Go가 미리 선언해 둔 숫자 타입들이 무엇이고, 각각 어떤 값을 보관하는지 정리해 볼게요.
출처: Go Specification
본문
먼저 큰 그림부터 잡아볼게요. Go의 숫자 타입은 크게 세 갈래로 나뉘어요.
- integer(정수)
- floating-point(부동소수점)
- complex(복소수)
각 타입은 각각 정수 값, 부동소수점 값, 복소수 값의 집합을 나타내고, 이 셋을 묶어서 numeric types(숫자 타입)이라고 불러요.
Go는 아키텍처와 무관하게 동일한 크기를 갖는, 미리 선언된 숫자 타입들을 제공합니다. 아래 표를 보면서 각 타입이 정확히 어떤 값을 담는지 확인해 볼게요.
uint8 the set of all unsigned 8-bit integers (0 to 255)
uint16 the set of all unsigned 16-bit integers (0 to 65535)
uint32 the set of all unsigned 32-bit integers (0 to 4294967295)
uint64 the set of all unsigned 64-bit integers (0 to 18446744073709551615)
int8 the set of all signed 8-bit integers (-128 to 127)
int16 the set of all signed 16-bit integers (-32768 to 32767)
int32 the set of all signed 32-bit integers (-2147483648 to 2147483647)
int64 the set of all signed 64-bit integers (-9223372036854775808 to 9223372036854775807)
float32 the set of all IEEE 754 32-bit floating-point numbers
float64 the set of all IEEE 754 64-bit floating-point numbers
complex64 the set of all complex numbers with float32 real and imaginary parts
complex128 the set of all complex numbers with float64 real and imaginary parts
byte alias for uint8
rune alias for int32
여기서 익숙한 이름이 보이죠. byte는 uint8의 별칭(alias)이고, rune은 int32의 별칭이에요. 그래서 이 둘을 제외하면, 대부분의 이름에 담긴 숫자(예: uint8의 8)가 곧 비트 수를 뜻해요. 정수 타입은 그 이름대로 n비트의 폭을 가지는데, 값의 표현 방식은 two's complement arithmetic(2의 보수) 을 따른답니다.
여기에 더해서, 구현(구현 아키텍처)에 따라 크기가 달라지는 미리 선언된 정수 타입도 있어요.
uint either 32 or 64 bits
int same size as uint
uintptr an unsigned integer large enough to store the uninterpreted bits of a pointer value
이 타입들의 특징을 짚어 볼게요.
uint는 32비트 또는 64비트 중 하나를 가져요.int는uint와 같은 크기예요.uintptr은 포인터 값의 비트를 해석 없이 그대로 담을 수 있을 만큼 큰 부호 없는 정수예요.
여기서 한 가지 꼭 알아두어야 할 점이 있는데요, 호환성(portability) 문제를 피하기 위해 모든 숫자 타입은 named types(이름 있는 타입) 이라는 점이에요. 즉 서로 구별되는 별개의 타입이라는 뜻이죠. 다만 예외가 두 개 있어요. 앞서 봤듯 byte는 uint8의 별칭이고, rune은 int32의 별칭이라서 이 둘만은 예외랍니다.
그래서 서로 다른 숫자 타입을 하나의 식이나 대입문에서 섞어 쓰려면 명시적 변환(explicit conversion)이 필수예요. 예를 들어 int32와 int는 어떤 아키텍처에서는 크기가 같을 수도 있지만, 그렇다고 같은 타입이 절대 아니에요.
더 알아보기
- 각 타입이 실제로 메모리에서 어떤 비트 폭을 갖는지는 위 표와 본문의 정의를 기준으로 확인할 수 있어요.
- 숫자 타입 사이의 변환 규칙(예: 정수끼리, 부동소수점과 정수 사이)에 대해서는 Conversions 섹션에서 더 자세히 다뤄요.
- 타입 전체의 그림이 궁금하다면 Types 섹션을 이어서 보면 좋아요.
- 포인터 값의 비트를 다룰 일이 생기면
uintptr타입이 필요해지는데, 이건 포인터 연산을 얘기할 때 자연스럽게 연결돼요.