누구나 로고
Online CoursesFor BusinessLibraryTrend InsightsFree coursesNoticesContactCareers
모든 강의를 무료로 볼 수 있어요. 회원가입 없이도 학습 가능합니다.
로그인무료 회원가입
강좌파이썬 기초 40강 — 처음 배우는 프로그래밍7강

7강 / 전체 14강

부동소수점(float)의 함정 — IEEE 754와 수치 오류의 진실

9분 읽기 조회 1

0.1+0.2가 0.3이 아닌 이유를 IEEE 754로 설명하고 수치 오류를 피하는 방법을 배운다.

🎯 이 강의에서 배우는 것

파이썬 REPL에서 0.1 + 0.2를 입력해보세요. 0.3이 아닌 0.30000000000000004가 출력됩니다. 이것은 파이썬의 버그가 아닙니다. 모든 현대 컴퓨터가 공유하는 IEEE 754 부동소수점 표준의 특성입니다. 이 강의에서는 이 현상의 근본 원인을 이해하고, 금융·과학 계산에서 정확한 결과를 얻는 방법을 배웁니다.

🔢 왜 0.1 + 0.2 ≠ 0.3 인가

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

# 더 많은 소수점 자리를 확인
print(f"{0.1:.55f}")
# 0.1000000000000000055511151231257827021181583404541015625
# 실제로 저장된 값은 0.1이 아님!

문제의 근원은 이진 소수에 있습니다. 10진수 0.1을 2진수 소수로 변환하면 무한 반복소수가 됩니다:

0.1 (10진수) = 0.00011001100110011... (2진수, 무한 반복)

컴퓨터는 유한한 비트로 이 무한 소수를 저장해야 하므로 적당한 위치에서 끊어버립니다. 이 끊김이 표현 오차(representation error)의 원인입니다. 10진수 1/3 = 0.333...이 종이에 완전히 쓸 수 없는 것과 같은 원리입니다.

📐 IEEE 754 배정밀도(64비트) 표준

파이썬의 float는 IEEE 754 배정밀도(double precision) 형식을 사용합니다. 64비트를 다음과 같이 나눕니다:

필드비트 수역할
부호(sign)1비트0 = 양수, 1 = 음수
지수(exponent)11비트2의 거듭제곱 (±1022)
가수(mantissa)52비트유효숫자 (약 15~17자리)
import sys

# 부동소수점 시스템 정보
info = sys.float_info
print(f"최대값: {info.max}")              # 1.7976931348623157e+308
print(f"최솟값(정상): {info.min}")        # 2.2250738585072014e-308
print(f"머신 엡실론: {info.epsilon}")     # 2.220446049250313e-16
# 머신 엡실론: 1.0에 더했을 때 1.0과 달라지는 가장 작은 수

# 특수 부동소수점 값
pos_inf = float('inf')
neg_inf = float('-inf')
nan = float('nan')

print(pos_inf)          # inf
print(neg_inf + 1)      # -inf
print(pos_inf + pos_inf) # inf
print(pos_inf - pos_inf) # nan (정의 불가)
print(nan == nan)       # False! (NaN은 자기 자신과도 같지 않음)
print(math.isnan(nan))  # True — NaN 확인하는 올바른 방법

import math
print(math.isinf(pos_inf))  # True

⚠️ 부동소수점 비교의 황금 규칙

부동소수점 값을 ==로 비교하는 것은 거의 항상 잘못된 방법입니다:

import math

# 절대로 하면 안 되는 방법
print(0.1 + 0.2 == 0.3)   # False — 의도와 다른 결과

# 올바른 방법 1: math.isclose()
print(math.isclose(0.1 + 0.2, 0.3))           # True
print(math.isclose(0.1 + 0.2, 0.3, rel_tol=1e-9))  # True

# rel_tol: 상대 허용 오차 (기본 1e-9)
# abs_tol: 절대 허용 오차 (기본 0.0)
# 두 조건 중 하나라도 만족하면 같다고 판단
# |a-b| <= max(rel_tol * max(|a|, |b|), abs_tol)

# 0에 가까운 값 비교 시 abs_tol 사용
a = 1e-10
b = 2e-10
print(math.isclose(a, b))                    # False (상대 오차 100%)
print(math.isclose(a, b, abs_tol=1e-9))     # True (절대 오차 1e-10)

# 올바른 방법 2: 반올림 후 비교 (간단한 경우)
print(round(0.1 + 0.2, 10) == round(0.3, 10))  # True

💰 금융 계산을 위한 decimal 모듈

금융 계산에서 0.1원의 오차도 허용되지 않습니다. 이런 경우 decimal 모듈을 사용합니다:

from decimal import Decimal, getcontext

# Decimal로 정확한 10진수 계산
print(Decimal("0.1") + Decimal("0.2"))    # 0.3  (정확!)
print(Decimal("0.1") + Decimal("0.2") == Decimal("0.3"))  # True

# 주의: float에서 Decimal로 변환하면 이미 손실된 값을 가져옴
print(Decimal(0.1))    # 0.1000000000000000055511151231257827021181583404541015625
print(Decimal("0.1"))  # 0.1  ← 문자열로 변환해야 정확

# 정밀도(유효 숫자 수) 설정
getcontext().prec = 50   # 50자리 정밀도
result = Decimal("1") / Decimal("3")
print(result)   # 0.33333333333333333333333333333333333333333333333333

# 반올림 방법 설정
from decimal import ROUND_HALF_UP, ROUND_HALF_EVEN
price = Decimal("2.455")
print(price.quantize(Decimal("0.01"), rounding=ROUND_HALF_UP))    # 2.46
print(price.quantize(Decimal("0.01"), rounding=ROUND_HALF_EVEN))  # 2.46

언제 decimal을 사용해야 하는가:

  • 금융 계산 (세금, 환율, 이자율)
  • 정확한 소수점 반올림이 필요한 경우
  • 법적으로 정확한 계산이 요구되는 경우

🔢 fractions 모듈: 유리수의 정확한 표현

from fractions import Fraction

# 정확한 유리수 표현
f = Fraction(1, 3)
print(f)              # 1/3
print(f + Fraction(1, 6))  # 1/2  (정확!)

# 문자열로 생성 (정확)
print(Fraction("0.1"))  # 1/10  — 정확!

# float에서 생성 (이미 손실된 값)
print(Fraction(0.1))    # 3602879701896397/36028797018963968  — 부정확!

# Fraction과 int의 혼합 연산
print(Fraction(1, 7) * 7)   # 1  (정확히 1)
print((1/7) * 7)             # 0.9999999999999999  (부동소수점 오차)

🎯 round()의 은행가 반올림

파이썬의 round()는 일반적인 반올림과 다르게 동작합니다:

# 파이썬의 round()는 "은행가 반올림(Banker's rounding)" 사용
# 0.5의 경우 가장 가까운 짝수로 반올림
print(round(0.5))   # 0  (짝수 방향)
print(round(1.5))   # 2  (짝수 방향)
print(round(2.5))   # 2  (짝수 방향)
print(round(3.5))   # 4  (짝수 방향)
print(round(4.5))   # 4  (짝수 방향)

# 이유: 통계적 편향 최소화
# 항상 올림 또는 항상 내림하면 많은 수의 반올림에서 편향 발생
# 짝수로 반올림하면 기대값이 0에 가까워짐

# 특정 소수점 자리로 반올림
print(round(3.14159, 2))   # 3.14
print(round(1234, -2))     # 1200 (음수 자리수 = 10의 거듭제곱)

# 다른 반올림 방법
import math
print(math.floor(2.9))    # 2  (바닥 방향)
print(math.ceil(2.1))     # 3  (천장 방향)
print(math.trunc(2.9))    # 2  (0 방향, 소수점 버림)
print(math.trunc(-2.9))   # -2 (0 방향, -3이 아님!)

⚠️ 자주 하는 실수

  • float 값을 == 로 비교: 반드시 math.isclose()를 사용하세요.
  • 금융 계산에 float 사용: 반드시 Decimal을 사용하세요. 1원도 틀리면 안 됩니다.
  • Decimal(0.1) 와 Decimal("0.1") 혼동: float로 먼저 변환하면 이미 오차가 포함됩니다. 문자열 "0.1"로 생성해야 정확합니다.
  • round()가 항상 .5를 올림한다고 가정: 파이썬은 은행가 반올림(가장 가까운 짝수)을 사용합니다.

📝 정리 및 다음 강의 예고

  • 부동소수점 오차는 이진 소수의 표현 한계로 발생하며, 모든 현대 컴퓨터가 공유합니다.
  • float 비교는 == 대신 math.isclose()를 사용하세요.
  • 금융 계산에는 decimal.Decimal, 정확한 유리수에는 fractions.Fraction을 사용하세요.
  • 파이썬의 round()는 은행가 반올림(가장 가까운 짝수)을 사용합니다.

다음 강의: 문자열(str)을 완전히 해부합니다. 유니코드가 무엇인지, UTF-8이 어떻게 작동하는지, 문자열이 왜 불변인지, 그리고 인코딩 오류를 어떻게 해결하는지 배웁니다.

관련 주제

  • IEEE 754 부동소수점
  • 0.1+0.2 오차 원인
  • math.isclose 비교
  • decimal 모듈
  • fractions 모듈
  • 은행가 반올림(round)
  • 개발·프로그래밍
  • 개발·프로그래밍 강의
  • 파이썬 기초 40강 — 처음 배우는 프로그래밍
  • 무료강의
  • 무료 온라인 강의
  • NUGUNA
  • 누구나
이전 6강정수(int) 완전 해부 — 임의 정밀도, 비트 연산, 정수 연산의 함정다음 8강 문자열(str) 완전 해부 — 유니코드, 불변성, 인코딩

댓글

0/1000

불러오는 중...

목차

  1. 1.컴퓨터는 어떻게 생각하는가 — 0과 1에서 프로그램까지
  2. 2.파이썬 생태계와 개발 환경 완전 설정
  3. 3.파이썬의 실행 모델 — 바이트코드, PVM, GIL
  4. 4.첫 프로그램 — print()와 input()으로 배우는 I/O 원리
  5. 5.변수란 무엇인가 — 이름표, 객체, 참조의 삼각관계
  6. 6.정수(int) 완전 해부 — 임의 정밀도, 비트 연산, 정수 연산의 함정
  7. 7.부동소수점(float)의 함정 — IEEE 754와 수치 오류의 진실
  8. 8.문자열(str) 완전 해부 — 유니코드, 불변성, 인코딩
  9. 9.불리언(bool)과 None — 진리값 시스템의 철학
  10. 10.연산자 완전 분석 — 우선순위, 결합성, 연산자 오버로딩 예고
  11. 11.f-string과 문자열 포맷팅 완전 정복
  12. 12.조건문 — 프로그램이 결정을 내리는 방법
  13. 13.while 루프 — 반복의 본질과 루프 설계
  14. 14.for 루프 — 이터레이션 프로토콜의 깊은 이해

Support

  • Notices
  • FAQ
  • Contact Us
  • Community

Help & Info

  • Terms of Service
  • Privacy Policy
  • Refund Policy

Services

  • About Us
  • Sign up
  • New courses
  • Free courses
누구나 로고
Terms of ServicePrivacy PolicyRefund Policy

상호명: NUGUNA  |  대표자: 정우진  |  사업자등록번호: 392-32-01817  |  통신판매업신고: 제 2026-서울양천-0564 호

주소: 서울특별시 양천구 목동서로 100  |  이메일: nugunapass@gmail.com  |  전화: 010-6395-3043

© 2026 NUGUNA. All rights reserved.

KB예금주인증관리자
HomeCoursesLibraryContactMY