Featured image of post [Python Cheatsheet] 47. decimal & fractions - 정밀 수치 연산

[Python Cheatsheet] 47. decimal & fractions - 정밀 수치 연산

파이썬 decimal과 fractions 모듈을 빠르게 사용하기 위한 치트시트입니다. 부동소수점 오차 없는 십진 연산, 분수 연산, 금융/과학 계산 패턴을 최소 예제로 정리합니다.

decimal부동소수점 오차 없는 십진 연산을, fractions분수 연산을 제공합니다. 금융 계산, 과학 계산 등 정밀도가 중요한 곳에서 사용합니다.

언제 이 치트시트를 보나?

  • 금융/회계 계산에서 정확한 소수점이 필요할 때
  • 부동소수점 오차를 피하고 싶을 때 (0.1 + 0.2 != 0.3)
  • 분수 연산이 필요할 때

부동소수점 문제

1
2
3
4
5
6
7
# float의 한계
print(0.1 + 0.2)         # 0.30000000000000004
print(0.1 + 0.2 == 0.3)  # False!

# 누적 오차
total = sum([0.1] * 10)
print(total)             # 0.9999999999999999

decimal 모듈

기본 사용

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
from decimal import Decimal

# 문자열로 생성 (권장)
d1 = Decimal('0.1')
d2 = Decimal('0.2')

print(d1 + d2)           # 0.3
print(d1 + d2 == Decimal('0.3'))  # True

# 정수로 생성
d3 = Decimal(10)

# float로 생성 (비권장 - 오차 포함)
d4 = Decimal(0.1)        # 0.1000000000000000055511151...

정밀도 설정

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
from decimal import Decimal, getcontext

# 전역 정밀도 설정
getcontext().prec = 4

print(Decimal('1') / Decimal('3'))  # 0.3333

# 높은 정밀도
getcontext().prec = 50
print(Decimal('1') / Decimal('3'))
# 0.33333333333333333333333333333333333333333333333333

반올림 모드

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
from decimal import Decimal, ROUND_HALF_UP, ROUND_DOWN, ROUND_CEILING

d = Decimal('2.345')

# 소수점 2자리로 반올림
d.quantize(Decimal('0.01'))                    # 2.35 (기본: ROUND_HALF_EVEN)
d.quantize(Decimal('0.01'), ROUND_HALF_UP)     # 2.35 (사사오입)
d.quantize(Decimal('0.01'), ROUND_DOWN)        # 2.34 (버림)
d.quantize(Decimal('0.01'), ROUND_CEILING)     # 2.35 (올림)

# 정수로 반올림
Decimal('2.5').quantize(Decimal('1'), ROUND_HALF_UP)  # 3

금융 계산 예제

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
from decimal import Decimal, ROUND_HALF_UP

def calculate_tax(price, tax_rate):
    """세금 계산 (소수점 이하 버림)"""
    price = Decimal(str(price))
    tax_rate = Decimal(str(tax_rate))
    tax = price * tax_rate
    return tax.quantize(Decimal('1'), ROUND_DOWN)

def calculate_total(items):
    """총액 계산"""
    total = sum(Decimal(str(item)) for item in items)
    return total.quantize(Decimal('0.01'), ROUND_HALF_UP)

# 사용
items = [10.99, 23.50, 5.99]
total = calculate_total(items)
print(f"Total: ${total}")  # Total: $40.48

tax = calculate_tax(40.48, 0.1)
print(f"Tax: ${tax}")      # Tax: $4

Decimal 유용한 메서드

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
from decimal import Decimal

d = Decimal('123.456')

# 부호, 숫자, 지수
d.as_tuple()           # DecimalTuple(sign=0, digits=(1,2,3,4,5,6), exponent=-3)

# 비교
d.compare(Decimal('100'))  # Decimal('1') (d > 100)

# 특수값 체크
Decimal('Infinity').is_infinite()  # True
Decimal('NaN').is_nan()            # True

# 정규화 (후행 0 제거)
Decimal('10.00').normalize()       # Decimal('1E+1')

# 복사 (부호 변경)
d.copy_abs()           # 123.456
d.copy_negate()        # -123.456

fractions 모듈

기본 사용

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
from fractions import Fraction

# 분수 생성
f1 = Fraction(1, 3)       # 1/3
f2 = Fraction(2, 6)       # 자동 약분 → 1/3
f3 = Fraction('3/4')      # 문자열로
f4 = Fraction(0.5)        # float로 (정확히 변환)
f5 = Fraction('0.125')    # 문자열 소수

print(f1)                 # 1/3
print(f1.numerator)       # 1
print(f1.denominator)     # 3

분수 연산

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
from fractions import Fraction

a = Fraction(1, 3)
b = Fraction(1, 4)

# 사칙연산
print(a + b)    # 7/12
print(a - b)    # 1/12
print(a * b)    # 1/12
print(a / b)    # 4/3

# 거듭제곱
print(a ** 2)   # 1/9

# 비교
print(a > b)    # True

float ↔ Fraction 변환

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
from fractions import Fraction

# float → Fraction
f = Fraction(0.5)
print(f)              # 1/2

# 0.1은 정확히 표현 불가
f = Fraction(0.1)
print(f)              # 3602879701896397/36028797018963968

# 문자열로 정확하게
f = Fraction('0.1')
print(f)              # 1/10

# Fraction → float
f = Fraction(1, 3)
print(float(f))       # 0.3333333333333333

근사 분수 (limit_denominator)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
from fractions import Fraction
import math

# π의 근사 분수
pi_fraction = Fraction(math.pi)
print(pi_fraction)
# 884279719003555/281474976710656 (정확한 변환)

# 분모 제한으로 근사
print(pi_fraction.limit_denominator(10))    # 22/7
print(pi_fraction.limit_denominator(100))   # 311/99
print(pi_fraction.limit_denominator(1000))  # 355/113

실용 예제

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
from fractions import Fraction

# 비율 계산
total = 100
parts = [Fraction(1, 4), Fraction(1, 3), Fraction(5, 12)]
print(sum(parts))  # 1 (모든 비율의 합)

# 각 파트의 실제 값
for i, part in enumerate(parts):
    print(f"Part {i+1}: {float(part * total):.2f}")
# Part 1: 25.00
# Part 2: 33.33
# Part 3: 41.67

Decimal vs Fraction vs float

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
from decimal import Decimal
from fractions import Fraction

# 1/3 표현
print(1/3)                    # 0.3333333333333333
print(Decimal('1') / 3)       # 0.3333333333333333... (정밀도만큼)
print(Fraction(1, 3))         # 1/3 (정확)

# 0.1 + 0.2
print(0.1 + 0.2)              # 0.30000000000000004
print(Decimal('0.1') + Decimal('0.2'))  # 0.3
print(Fraction('0.1') + Fraction('0.2'))  # 3/10

자주 하는 실수

1. float로 Decimal 생성

1
2
3
4
5
6
7
from decimal import Decimal

# 잘못된 방법
bad = Decimal(0.1)     # 오차 포함

# 올바른 방법
good = Decimal('0.1')  # 정확

2. 혼합 연산

1
2
3
4
5
6
7
from decimal import Decimal

# float과 Decimal 혼합 불가
# Decimal('1.5') + 0.5  # TypeError

# 명시적 변환 필요
Decimal('1.5') + Decimal('0.5')  # OK

한눈에 정리

타입용도장점단점
float일반 계산빠름오차 있음
Decimal금융/정밀 계산십진 정확도느림
Fraction분수/비율무손실 유리수제한된 용도

참고