WIPIVERSE

signed와 unsigned

signed와 unsigned

signed(부호 있는)와 unsigned(부호 없는)는 컴퓨터 과학에서 정수형 데이터 타입이 부호(양수/음수)를 표현할 수 있는지 여부를 구분하는 분류이다. 이 구분은 메모리 내 비트 패턴의 해석 방식과 표현 가능한 수의 범위를 결정한다.

1. signed (부호 있는 정수)

  • 정의: 최상위 비트(MSB, Most Significant Bit)를 부호 비트(Sign Bit)로 사용하여 양수, 0, 음수를 모두 표현하는 방식이다.
  • 표현 방식: 현대 컴퓨터 시스템에서는 2의 보수(Two's Complement) 방식이 사실상 표준으로 사용된다. 1의 보수나 부호-크기 방식이 역사적으로 존재했으나 현재는 거의 쓰이지 않는다.
  • 범위: n비트 정수일 때 표현 범위는 $-2^{n-1}$ 부터 $2^{n-1}-1$ 까지이다.
    • 예: 8비트 signed 정수(int8_t)의 범위는 -128 ~ 127이다.
  • 오버플로: 표현 범위를 초과하는 연산 발생 시 정의되지 않은 행동(Undefined Behavior, C/C++ 표준) 또는 래핑(Wrapping, Java 등) 등이 발생할 수 있다.

2. unsigned (부호 없는 정수)

  • 정의: 모든 비트를 크기(Magnitude)를 나타내는 데 사용하여 0과 양수만 표현하는 방식이다. 부호 비트가 별도로 존재하지 않는다.
  • 표현 방식: 순수 이진법(Pure Binary)으로 해석한다.
  • 범위: n비트 정수일 때 표현 범위는 $0$ 부터 $2^n-1$ 까지이다.
    • 예: 8비트 unsigned 정수(uint8_t)의 범위는 0 ~ 255이다.
  • 오버플로: 산술 연산 시 모듈로 $2^n$ 연산(Modulo Arithmetic, 래핑)으로 정의되어 있는 경우가 많다(C/C++ 표준 보장).

3. 주요 차이점 및 비교

특징 Signed Unsigned
표현 수 음수, 0, 양수 0, 양수
최상위 비트(MSB) 부호 비트 (1: 음수, 0: 양수/0) 데이터 비트 (값의 일부)
동일 비트 폭에서 최대값 약 절반 ($2^{n-1}-1$) 약 2배 ($2^n-1$)
비교 연산 산술 비교 (Arithmetic Comparison) 논리 비교 (Logical Comparison)
오른쪽 시프트(>>) 산술 시프트 (부호 비트 복사, 구현 정의) 논리 시프트 (0 채움)

4. 프로그래밍 언어에서의 사용

  • C/C++: int, short, long, char 등은 구현 정의(보통 signed)이며, 명시적으로 signed int, unsigned int 등으로 지정 가능. char의 부호 여부는 컴파일러/아키텍처 의존적.
  • Java: byte, short, int, long은 모두 signed만 지원. char는 unsigned 16비트 유니코드 문자용. Java 8부터 Integer.toUnsignedString 등 unsigned 연산 지원 메서드 제공.
  • C#: sbyte, short, int, long (signed) / byte, ushort, uint, ulong (unsigned) 키워드 별도 제공.
  • Rust: i8~i128, isize (signed) / u8~u128, usize (unsigned) 명시적 구분.
  • Go: int8~int64, int (signed) / uint8~uint64, uint (unsigned) 명시적 구분.
  • Python 3: 정수 타입(int)은 부호가 있으며 임의 정밀도(Arbitrary Precision)를 지원하여 오버플로가 발생하지 않음. 별도 unsigned 타입 없음.

5. 타입 변환 및 승격 (Type Conversion/Promotion)

  • 부호 있는/없는 타입 혼용 연산: C/C++ 등에서 signed와 unsigned 피연산자가 섞이면, 보통 unsigned로 승격(Integer Promotion)되어 연산이 수행된다. 이 경우 의도치 않은 결과(예: 음수가 매우 큰 양수로 해석됨)가 발생할 수 있어 주의가 필요하다.
  • 명시적 캐스팅: 타입 변환 시 비트 패턴은 유지되지만 해석 방식이 바뀐다. (예: uint8_t 255(0xFF)를 int8_t로 캐스팅 시 -1이 됨).

6. 용례 및 선택 가이드

  • Signed 사용: 일반적인 산술 연산, 음수 값이 의미 있는 데이터(온도, 좌표, 차이값 등), 인덱스가 음수가 될 수 있는 알고리즘.
  • Unsigned 사용: 비트 조작, 메모리 주소/크기(size_t), 카운터(루프 변수 등, 단 감소 연산 시 언더플로 주의), 비트마스크, 네트워크 패킷/파일 포맷 파싱 등 원시 데이터 처리.
  • 모범 사례: 많은 코딩 가이드라인(C++ Core Guidelines 등)은 "음수가 필요하지 않더라도 unsigned를 남용하지 말고, 기본적으로 signed(int 등)를 사용하라"고 권고하기도 한다. 부호 없는 타입과의 혼용으로 인한 버그를 방지하기 위함이다. 반면, 임베디드 시스템이나 비트 단위 연산이 빈번한 도메인에서는 unsigned 사용이 표준적이다.
둘러보기

더 찾아볼 만한 주제

    전체 문서 보기