C# 정수 계열 숫자 형식

C# 정수 계열 숫자 형식 (Integral numeric types)

프로그램에서 가장 자주 만나는 숫자는 아마 정수일 거예요. C#에서 정수를 다루는 형식(타입)이 어떤 것들이 있고, 각 형식이 담을 수 있는 값의 범위와 메모리 크기가 어떻게 다른지, 리터럴은 어떻게 쓰는지 차근차근 짚어볼게요. 이 글을 끝까지 보면 "정수 형식은 뭘 골라야 하지?"라는 고민이 한결 가벼워질 거예요.

출처: Microsoft Learn

본문

정수 계열 숫자 형식(integral numeric types)은 정수를 나타내요. 모든 정수 계열 숫자 형식은 값 형식(value type)이고, 단순 형식(simple type)에 속하며, 리터럴로 초기화해요. 그리고 이 형식들은 모두 산술(arithmetic), 비트 논리(bitwise logical), 비교(comparison), 같음(equality) 연산자를 지원해요.

정수 형식의 특성

C#이 제공하는 미리 정의된 정수 형식은 다음과 같아요.

C# 형식/키워드 범위 크기 .NET 형식
sbyte -128 ~ 127 부호 있는 8비트 정수 xref:System.SByte?displayProperty=nameWithType
byte 0 ~ 255 부호 없는 8비트 정수 xref:System.Byte?displayProperty=nameWithType
short -32,768 ~ 32,767 부호 있는 16비트 정수 xref:System.Int16?displayProperty=nameWithType
ushort 0 ~ 65,535 부호 없는 16비트 정수 xref:System.UInt16?displayProperty=nameWithType
int -2,147,483,648 ~ 2,147,483,647 부호 있는 32비트 정수 xref:System.Int32?displayProperty=nameWithType
uint 0 ~ 4,294,967,295 부호 없는 32비트 정수 xref:System.UInt32?displayProperty=nameWithType
long -9,223,372,036,854,775,808 ~ 9,223,372,036,854,775,807 부호 있는 64비트 정수 xref:System.Int64?displayProperty=nameWithType
ulong 0 ~ 18,446,744,073,709,551,615 부호 없는 64비트 정수 xref:System.UInt64?displayProperty=nameWithType
nint 플랫폼에 따라 다름(런타임에 계산) 부호 있는 32비트 또는 64비트 정수 xref:System.IntPtr?displayProperty=nameWithType
nuint 플랫폼에 따라 다름(런타임에 계산) 부호 없는 32비트 또는 64비트 정수 xref:System.UIntPtr?displayProperty=nameWithType

표에서 마지막 두 줄을 제외한 모든 줄에서, 맨 왼쪽의 C# 형식 키워드는 해당 .NET 형식의 별칭(alias)이에요. 다시 말해 키워드와 .NET 형식 이름은 서로 바꿔 쓸 수 있어요. 예를 들어 아래 두 선언은 사실상 같은 형식의 변수를 만듭니다.

int a = 123;
System.Int32 b = 123;

표 마지막 두 줄의 nintnuint네이티브 크기 정수(native-sized integers)예요. 네이티브 크기 정수를 정의하려면 nintnuint 컨텍스트 키워드를 사용해요. 이 형식들은 32비트 프로세스에서 실행 중일 때는 32비트 정수, 64비트 프로세스에서 실행 중일 때는 64비트 정수가 됩니다. 주로 interop 시나리오나 저수준 라이브러리, 그리고 정수 연산을 많이 수행하는 곳에서 성능을 최적화할 때 사용해요.

네이티브 크기 정수 형식은 내부적으로 .NET 형식인 xref:System.IntPtr?displayProperty=nameWithTypexref:System.UIntPtr?displayProperty=nameWithType로 표현돼요. nintnuint는 그 밑에 깔린 형식의 별칭입니다.

각 정수 형식의 기본값은 0, 즉 0이에요.

각 정수 형식에는 그 형식이 담을 수 있는 최솟값과 최댓값을 알려주는 MinValueMaxValue 속성이 있어요. 이 속성들은 네이티브 크기 형식(nint, nuint)을 제외하고는 모두 컴파일 타임 상수예요. 네이티브 크기 형식의 MinValueMaxValue는 런타임에 계산되는데, 크기가 프로세스 설정에 따라 달라지기 때문이에요.

위아래 한계가 없는 부호 있는 정수를 표현해야 한다면 xref:System.Numerics.BigInteger?displayProperty=nameWithType 구조체를 사용해요.

정수 리터럴

정수 리터럴은 다음 세 가지 형태 중 하나로 쓸 수 있어요.

  • 10진수: 접두사 없이
  • 16진수: 0x 또는 0X 접두사 사용
  • 2진수: 0b 또는 0B 접두사 사용

아래 코드가 각각의 예시를 보여줍니다.

var decimalLiteral = 42;
var hexLiteral = 0x2A;
var binaryLiteral = 0b_0010_1010;

앞의 예시에서 _자릿수 구분자(digit separator)로 쓴 것도 보셨죠. 자릿수 구분자는 모든 종류의 숫자 리터럴에 사용할 수 있어요.

접미사에 따라 정수 리터럴의 형식이 정해지는 규칙은 다음과 같습니다.

  • 접미사가 없으면, 값이 표현될 수 있는 형식 중 다음 순서에서 처음 만나는 형식으로 컴파일러가 정해요: int, uint, long, ulong.

    [!NOTE] 컴파일러는 리터럴을 항상 양수 값으로 해석해요. 예를 들어 0xFF_FF_FF_FF라는 리터럴은 int 형식의 -1과 같은 비트 표현을 갖고 있지만, uint 형식의 4,294,967,295로 해석됩니다. 특정 형식의 값이 필요하면 리터럴을 그 형식으로 캐스팅해요. 리터럴 값이 대상 형식에 표현될 수 없다면 unchecked 연산자를 사용해요. 예를 들어 unchecked((int)0xFF_FF_FF_FF)-1을 만들어냅니다.

  • 리터럴에 U 또는 u 접미사가 있으면, 값이 표현될 수 있는 형식 중 다음 순서에서 처음 만나는 형식으로 컴파일러가 정해요: uint, ulong.

  • 리터럴에 L 또는 l 접미사가 있으면, 값이 표현될 수 있는 형식 중 다음 순서에서 처음 만나는 형식으로 컴파일러가 정해요: long, ulong.

    [!NOTE] 접미사로 소문자 l을 쓸 수 있긴 해요. 그런데 l은 숫자 1과 혼동될 수 있어서 컴파일러 경고가 발생해요. 명확하게 쓰려면 L을 사용하세요.

  • 리터럴에 UL, Ul, uL, ul, LU, Lu, lU, lu 중 하나의 접미사가 있으면, 컴파일러가 형식을 ulong으로 정해요.

정수 리터럴이 표현하는 값이 xref:System.UInt64.MaxValue?displayProperty=nameWithType를 초과하면 컴파일러 오류 CS1021이 발생해요.

컴파일러가 정수 리터럴의 형식을 int로 결정했고, 리터럴이 표현하는 값이 대상 형식의 범위 안에 있다면, 그 값은 sbyte, byte, short, ushort, uint, ulong, nint, nuint로 묵시적으로 변환될 수 있어요.

byte a = 17;
byte b = 300;   // CS0031: Constant value '300' cannot be converted to a 'byte'

앞의 예시에서 보듯, 리터럴의 값이 대상 형식의 범위를 벗어나면 컴파일러 오류 CS0031이 발생해요.

캐스트를 사용해 정수 리터럴이 표현하는 값을 리터럴의 결정된 형식이 아닌 다른 형식으로 변환할 수도 있어요.

var signedByte = (sbyte)42;
var longVariable = (long)42;

변환 (Conversions)

모든 정수 계열 숫자 형식은 다른 어떤 정수 계열 숫자 형식으로든 변환할 수 있어요. 대상 형식이 원본 형식의 모든 값을 저장할 수 있으면 변환은 묵시적(implicit)이에요. 그렇지 않으면 캐스트 식를 사용해 명시적(explicit) 변환을 수행해요. 자세한 내용은 기본 제공 숫자 변환을 참고하세요.

네이티브 크기 정수

네이티브 크기 정수 형식은 저장 크기가 대상 머신의 자연 정수 크기와 일치하기 때문에 특별한 동작을 보여요.

C# 언어 사양

자세한 내용은 C# 언어 사양의 다음 섹션을 참고하세요.

더 알아보기