본문으로 건너뛰기
김신건의 로그

[Python] int: 임의 정밀도 정수

· 수정 · 📖 약 2분 · 973자/단어 #python #int #number #integer #basics #cpython-internals
python int, python integer, 임의 정밀도, arbitrary precision, bignum, PyLongObject, small int cache

정의

Python의 int는 **임의 정밀도 정수(arbitrary precision integer)**다. C의 int32/int64처럼 비트 폭이 고정되지 않으며, 메모리가 허용하는 한 무한히 큰 정수를 표현한다. 내부는 PyLongObject 구조체로, 30비트 단위의 디지트 배열로 구현되어 있다.

CPython 내부 구조: PyLongObject

Python 3.12부터 int 내부 구조가 크게 최적화되었다.

flowchart TD
    A["PyLongObject"] --> B["ob_refcnt\n(Py_ssize_t)"]
    A --> C["ob_type\n(*PyTypeObject = &PyLong_Type)"]
    A --> D["lv_tag (uintptr_t)\n3.12+ compact representation"]
    D --> E["sign: 2 bits"]
    D --> F["ndigits: 상위 bits"]
    A --> G["ob_digit[]\n30-bit limbs (LSB first)"]
    G --> H["digit[0] (최하위)"]
    G --> I["digit[1]"]
    G --> J["..."]

3.12 이전: ob_size

/* CPython 3.11 이전 */
typedef struct {
    PyObject_VAR_HEAD  /* ob_refcnt, ob_type, ob_size */
    digit ob_digit[1]; /* 30비트 limb 배열 */
} PyLongObject;

ob_size가 양수면 양수, 음수면 음수, 0이면 0.

3.12+: Compact Representation

/* CPython 3.12+ */
typedef struct {
    uintptr_t lv_tag;  /* 하위 3비트: tag, 나머지: ndigits / 값 */
    digit ob_digit[1];
} PyLongObject;

Compact int: limb 1개(-2^30 ~ 2^30-1)이면 ob_digit을 별도 메모리 없이 인라인 저장. 일반 정수의 메모리 풋프린트 30% 감소.

Small Int Cache: -5 ~ 256

CPython은 -5 ~ 256 범위의 정수를 인터프리터 시작 시 미리 생성해 캐싱한다. 같은 값이면 동일한 객체를 반환한다.

flowchart LR
    A["정수 리터럴 또는\n`int()` 호출"] --> B{"-5 <= n <= 256?"}
    B -->|"Yes"| C["small_ints[] 캐시에서\n기존 객체 반환"]
    B -->|"No"| D["새 PyLongObject 할당"]
    C --> E["ob_refcnt만 증가"]
    D --> F["heap 메모리 사용"]
python
# 캐시 범위 내: 동일 객체
a, b = 256, 256
print(a is b)         # True

# 캐시 범위 밖: 별도 객체
c, d = 257, 257
print(c is d)         # False (보통. REPL 내 동일 코드 객체라면 True일 수도)

# 항상 ==로 비교
print(c == d)         # True

# sys.intern 트릭 (문자열 유사)
x = sys.intern("hello" * 100)   # 안 됨. int는 intern 없음
결과
True
False
True

왜 -5 ~ 256인가

CPython 소스 Objects/longobject.c:

#define NSMALLPOSINTS   257   /* 0 ~ 256 */
#define NSMALLNEGINTS   5    /* -5 ~ -1 */
static PyLongObject small_ints[NSMALLNEGINTS + NSMALLPOSINTS];

-5는 일반적인 오프셋(-1 같은 에러 코드)에 쓰이고, 256은 바이트 값(0x00~0xFF)과 ASCII 범위를 커버하기 위한 설계.

IMPORTANT

is는 객체 동일성 비교다. 정수 동등성 비교는 항상 == 사용. 캐시 범위가 구현 세부사항이므로 is에 의존하는 코드는 이식성이 없다.

기본 사용

python
x = 42
y = -7
z = 10 ** 100   # googol
big = 2 ** 1000

print(x, y)
print(z)
print(big.bit_length(), "bits")
결과
42 -7
10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
1001 bits

2 ** 1000도 오버플로 없이 동작한다. C/Java라면 long도 표현 못 한다.

C long vs Python int

항목C int64_tPython int
크기64비트 고정무제한 (메모리 한계)
오버플로정의되지 않은 동작 / 래핑없음
메모리8 bytes28 bytes (작은 int 기본)
속도레지스터 연산객체 할당 + GC
사용처시스템/네이티브 코드Python 연산

NumPy np.int64는 C int64_t를 래핑해 고정 크기 + 고속 배열 연산을 제공. int 단독 계산보다 배열 연산은 100배+ 빠를 수 있다.

진법 리터럴

binary = 0b1010      # 2진수: 10
octal  = 0o17        # 8진수: 15
hexa   = 0xff        # 16진수: 255
under  = 1_000_000   # 가독성 언더스코어 (PEP 515)

진법 변환 함수:

python
n = 255
print(bin(n))    # 2진
print(oct(n))    # 8진
print(hex(n))    # 16진

print(int("ff", 16))     # 16진 문자열 -> int
print(int("1010", 2))    # 2진 문자열 -> int
결과
0b11111111
0o377
0xff
255
10

정수 연산

연산자의미예시
+, -, *사칙연산5 + 3 == 8
/부동소수 나눗셈7 / 2 == 3.5
//몫 (floor division)7 // 2 == 3, -7 // 2 == -4
%나머지7 % 2 == 1
**거듭제곱2 ** 10 == 1024
divmod(a, b)몫/나머지 동시divmod(7, 2) == (3, 1)

//수학적 floor라 음수에서 0으로 자르는 C와 다르다.

python
print(-7 // 2)    # -4 (C: -3)
print(-7 % 2)     # 1  (C: -1)
# 불변식: (a // b) * b + (a % b) == a
print((-7 // 2) * 2 + (-7 % 2))
결과
-4
1
-7

비트 연산

int는 무한 길이 2의 보수로 비트 연산을 지원한다.

a = 0b1100   # 12
b = 0b1010   # 10

print(a & b)     # 8  (AND)
print(a | b)     # 14 (OR)
print(a ^ b)     # 6  (XOR)
print(~a)        # -13 (NOT: -(a+1))
print(a << 2)    # 48
print(a >> 1)    # 6

비트 조작 패턴

# 특정 비트 설정/해제/토글
n = 0b1100

# 비트 k 설정 (set)
k = 1
n |= (1 << k)     # 0b1110

# 비트 k 해제 (clear)
n &= ~(1 << k)    # 0b1100

# 비트 k 토글
n ^= (1 << k)     # 0b1110

# 비트 k 읽기
bit = (n >> k) & 1

# 최하위 비트 제거 (Brian Kernighan)
n &= n - 1

# 2의 거듭제곱 여부
is_pow2 = n > 0 and (n & (n - 1)) == 0

유용한 메서드 (3.12+ 기준)

n = 255
n.bit_length()             # 8 (2진수 표현에 필요한 비트 수)
n.bit_count()              # 8 (3.10+: 1인 비트 개수, popcount)
n.to_bytes(2, "big")       # b'\x00\xff'
n.to_bytes(2, "little")    # b'\xff\x00'
int.from_bytes(b"\x00\xff", "big")   # 255

# 3.11+: bit_count (popcount)
(0b10110).bit_count()      # 3

# sys.int_info
import sys
print(sys.int_info)
# sys.int_info(bits_per_digit=30, sizeof_digit=4, ...)

bool은 int의 서브클래스

TrueFalse는 각각 int(1), int(0)이다.

python
print(True + 1)
print(isinstance(True, int))
print(sum([True, False, True, True]))
결과
2
True
3

sum(condition_list)로 조건을 만족하는 원소 수를 세는 관용구가 흔히 쓰인다.

성능 노트

  • 64비트 이하 정수는 거의 C와 비슷한 속도지만, 매번 객체를 할당하므로 NumPy int64 배열이 훨씬 빠르다.
  • 거대한 정수 곱셈은 Karatsuba 알고리즘을 사용 (n이 작으면 초등 알고리즘, 매우 크면 Toom-Cook 3-way).
  • Python 3.11+에서 int(s) 변환의 DoS 방지 패치: 4300자 이상 기본 ValueError.
# 3.11+: 큰 문자열 int 변환 제한
import sys
print(sys.get_int_max_str_digits())   # 4300 (기본)
sys.set_int_max_str_digits(0)         # 제한 해제 (위험)

함정

is로 정수 비교

# WRONG
def is_admin(level):
    return level is 1   # SyntaxWarning (3.8+)

# OK
def is_admin(level):
    return level == 1

WARNING

is는 객체 ID 비교. 캐시 범위(-5~256) 밖에서는 동일 값이어도 False. 정수/문자열 비교는 항상 ==.

정수 나눗셈

result = 7 / 2     # 3.5 (float!)
result = 7 // 2    # 3   (int)

Python 2 코드 포팅 시 /가 정수 나눗셈이었던 동작이 3에서 변경됨. 타입이 중요하다면 // 명시.

오버플로 없는 정수의 메모리

import sys
print(sys.getsizeof(0))          # 24 bytes
print(sys.getsizeof(2 ** 30))    # 28 bytes
print(sys.getsizeof(2 ** 60))    # 32 bytes
print(sys.getsizeof(2 ** 300))   # 68 bytes

큰 수는 그만큼 메모리를 차지한다. 암호화 등 큰 수 연산 대량 수행 시 메모리 감시.

관련 위키

이 글의 용어 (7개)
[Python] 가비지 컬렉션: refcount + 순환 GCpython
정의 CPython의 메모리 관리는 두 메커니즘의 조합이다. 1. Reference counting: 모든 객체에 참조 카운트( )가 있고, 카운트가 0이 되면 즉시 해제. 즉각…
[Python] 바이트코드와 dis 모듈python
정의 CPython 인터프리터는 Python 소스를 바이트코드로 컴파일해 평가 루프( 의 메인 switch)에서 실행한다. 바이트코드는 스택 기반 가상 머신을 위한 작은 명령어 …
[Python] 연산자: 비교, 산술, 비트, 멤버십python
분류 | 종류 | 연산자 | 예시 | |------|--------|------| | 산술 | | , (행렬곱) | | 비교 | | | | 동일성 | | | | 멤버십 | | |…
[Python] float, Decimal, Fractionpython
정의 Python의 실수 산술은 세 가지 타입을 제공한다. - : IEEE 754 double precision (64비트). C 과 동일. 빠르지만 부정확. - : 임의 정밀도…
[Python] None, bool, Truthinesspython
None 은 값 없음을 나타내는 싱글톤 객체다. 타입은 . 모든 함수가 명시적 return 없으면 을 반환한다. None 비교는 로 PEP 8이 명시한 규칙. NumPy 배열 같…
[Python] tracemalloc, sys.getsizeof: 메모리 프로파일링python
정의 은 Python의 메모리 할당 추적기(PEP 454, 3.4+). 어디서 메모리가 할당되었는지 파일·줄 단위로 통계 수집. 메모리 누수 추적과 핫스팟 발견에 표준 도구. 기…
Pythonpython
정의 Python은 Guido van Rossum이 1991년 발표한 인터프리터 방식의 동적 타이핑 언어다. 읽기 쉬운 문법과 방대한 라이브러리 생태계로 웹 백엔드, 데이터 분석…

💬 댓글

사이트 검색 / 명령어

검색

스크롤 = 확대/축소 · 드래그 = 이동 · 0 = 원래 크기 · ESC = 닫기