BioPlayground

🧬
목록으로

정수 오버플로우와 부동소수점 한계

컴퓨터가 왜 0.1 + 0.2 ≠ 0.3인지, 정수 오버플로우가 왜 발생하는지 원리를 이해합니다.

중급
|
10
|
검증 완료 (2026-07)
정수 오버플로우부동소수점IEEE 754정밀도 손실수치 계산
진행률0/11 (0%)

정수 오버플로우와 부동소수점 한계

이 토픽을 마치면

정수 오버플로우가 왜 발생하는지 이해하고, 부동소수점의 정밀도 한계를 설명할 수 있으며, 수치 비교 시 주의할 점을 알게 됩니다.


컴퓨터는 숫자를 틀린다

python
print(0.1 + 0.2) # 0.30000000000000004
print(0.1 + 0.2 == 0.3) # False!

이것은 Python의 버그가 아닙니다. C, Java, JavaScript, Rust — 모든 언어에서 동일합니다. 컴퓨터가 소수를 저장하는 방식 때문입니다.


정수 오버플로우

비트 수와 범위

컴퓨터에서 정수는 정해진 비트 수로 표현됩니다.

text
8비트 부호 없는 정수:  0 ~ 255
8비트 부호 있는 정수:  -128 ~ 127
32비트 부호 있는 정수: -2,147,483,648 ~ 2,147,483,647
64비트 부호 있는 정수: -9.2 × 10^18 ~ 9.2 × 10^18

범위를 넘으면 오버플로우가 발생합니다.

오버플로우의 실체

8비트 부호 없는 정수에서 255 + 1은?

text
11111111  (255)
+ 00000001  (1)
----------
 100000000  (256 — 하지만 9번째 비트가 잘림!)
= 00000000  (0)

255 다음이 0으로 돌아갑니다! 이것이 오버플로우입니다. 자동차 주행거리계가 999,999에서 000,000으로 돌아가는 것과 같습니다.

실제 사고 사례

c
// C 언어 — 32비트 정수 오버플로우
int balance = 2147483647;  // 최대값
balance = balance + 1;      // -2147483648! (양수 → 음수로 뒤집힘)
  • 아리안 5 로켓 폭발 (1996): 64비트 값을 16비트로 변환 → 오버플로우 → 로켓 자폭
  • 갱남스타일 YouTube 조회수 (2014): 32비트 정수 한계(21억) 초과 → Google이 64비트로 변경

Python은 오버플로우가 없다

python
# Python은 정수 크기에 제한이 없음
big = 2 ** 100
print(big) # 1267650600228229401496703205376
bigger = 2 ** 1000
print(len(str(bigger))) # 302자리 수!

Python은 필요에 따라 메모리를 자동으로 확장합니다. 하지만 C, Java, JavaScript 등 대부분의 언어에서는 정수 크기가 고정되어 있으므로 오버플로우에 주의해야 합니다.

NumPy도 오버플로우가 발생합니다 — NumPy 배열은 C 스타일 고정 크기 정수를 사용하기 때문입니다:

python
import numpy as np
a = np.int32(2147483647)
print(a + 1) # -2147483648 (오버플로우!)
b = np.int64(2147483647)
print(b + 1) # 2147483648 (64비트에서는 안전)

부동소수점 — IEEE 754

0.1을 2진법으로 변환하면

10진법에서 1/3 = 0.333333...이 무한소수인 것처럼, 2진법에서 0.1 = 0.0001100110011...이 무한소수입니다.

text
0.1 (10진법) = 0.00011001100110011001100110011... (2진법, 무한 반복)

컴퓨터는 유한한 비트(64비트)로 이 무한소수를 저장하므로, 어딘가에서 잘라야 합니다. 이 잘림이 오차의 원인입니다.

IEEE 754 — 64비트 부동소수점

text
[1비트 부호][11비트 지수][52비트 가수]
 0          01111111011  1001100110011001100110011001100110011001100110011010

부호: 0 (양수)
지수: 실제 지수를 결정
가수: 유효 숫자 (52비트 ≈ 15~17자리 정밀도)
python
# 15~17자리까지만 정확
print(f"{0.1:.20f}") # 0.10000000000000000555
print(f"{0.2:.20f}") # 0.20000000000000001110
print(f"{0.3:.20f}") # 0.29999999999999998890

0.1도 정확하지 않고, 0.2도 정확하지 않으므로, 더한 결과도 정확하지 않습니다.


실전 문제와 해결

1. 비교 — 절대 ==로 비교하지 않는다

python
# Bad
if 0.1 + 0.2 == 0.3:
print("Equal") # 실행 안 됨!
# Good — 허용 오차(epsilon) 사용
epsilon = 1e-9
if abs((0.1 + 0.2) - 0.3) < epsilon:
print("Equal") # 실행됨
# Better — math.isclose 사용
import math
if math.isclose(0.1 + 0.2, 0.3):
print("Equal") # 실행됨

2. 누적 오차 — 반복 계산에서 커진다

python
total = 0.0
for _ in range(1000):
total += 0.1
print(total) # 99.99999999999857 (100이 아님!)
print(f"Error: {abs(total - 100):.15f}") # 0.000000000001432

3. 금융 계산 — Decimal 사용

python
from decimal import Decimal
# float — 오차 발생
price = 0.1 + 0.2
print(price) # 0.30000000000000004
# Decimal — 정확
price = Decimal('0.1') + Decimal('0.2')
print(price) # 0.3 (정확!)
# 주의: 문자열로 초기화해야 함
Decimal(0.1) # Decimal('0.1000000000000000055511151231257827021181583404541015625')
Decimal('0.1') # Decimal('0.1') — 정확

금액, 세금, 이자율 — 정확성이 중요한 계산에서는 float 대신 Decimal을 씁니다.


JavaScript의 숫자 — 정수도 부동소수점

JavaScript에는 정수 타입이 없습니다. 모든 숫자가 64비트 부동소수점(IEEE 754)입니다.

javascript
// JavaScript — 큰 정수에서 정밀도 상실
console.log(9007199254740992 === 9007199254740993);  // true!
// 두 수가 같다고 판단 — 52비트 가수 범위를 넘었기 때문

console.log(Number.MAX_SAFE_INTEGER);  // 9007199254740991 (2^53 - 1)

이 때문에 JavaScript에서 BigInt가 도입되었습니다:

javascript
const big = 9007199254740993n;  // BigInt 리터럴 (n 접미사)
console.log(big === 9007199254740993n);  // true — 정확

Twitter(현 X)의 트윗 ID가 문자열로 전달되는 이유도 이것입니다 — JSON의 숫자로 파싱하면 정밀도가 손실되므로, API가 ID를 문자열 "id_str"로도 제공합니다.


특수 값

python
# 무한대
print(float('inf')) # inf
print(float('inf') + 1) # inf
print(float('inf') * -1) # -inf
# NaN (Not a Number)
print(float('nan')) # nan
print(float('nan') == float('nan')) # False! (NaN은 자기 자신과도 같지 않음)
import math
print(math.isnan(float('nan'))) # True (올바른 확인 방법)
print(math.isinf(float('inf'))) # True

NaN은 유일하게 자기 자신과 ==False인 값입니다. NaN 확인에는 반드시 math.isnan()을 사용합니다.


핵심 정리

개념정리
정수 오버플로우고정 비트 수를 넘으면 값이 뒤집힘. Python은 예외 (자동 확장)
IEEE 75464비트 부동소수점 표준. 52비트 가수 ≈ 15~17자리 정밀도
0.1 + 0.2 ≠ 0.30.1이 2진법에서 무한소수 → 저장 시 잘림 → 오차 발생
비교== 대신 math.isclose() 사용
금융 계산float 대신 Decimal 사용
NaN자기 자신과도 같지 않은 값. math.isnan()으로 확인

"컴퓨터는 계산을 정확히 한다"는 오해입니다. 컴퓨터는 유한한 비트로 무한한 수를 표현하기 때문에, 근본적으로 정밀도에 한계가 있습니다. 이 한계를 이해하면, 금융 시스템에서 Decimal을 쓰는 이유, 게임에서 좌표가 "떨리는" 이유, 과학 계산에서 오차 분석이 필요한 이유가 명확해집니다.

실전 규칙: 정수는 Python이라면 안전, 다른 언어는 범위를 확인. 소수는 == 절대 금지, 금융은 Decimal. NaN은 math.isnan()으로 확인.