08. 여덟 번째 수업: 컴퓨터와 유리수 (Rationals in Computers)

지금까지 우리는 유리수가 분자와 분모의 완벽한 비율($\frac{a}{b}$)로 존재한다는 것을 배웠습니다. 그런데 우주선의 궤도를 시뮬레이션하거나 금융 데이터를 다루는 실제 프로그래머들은 유리수를 어떻게 컴퓨터 메모리에 저장할까요? 안타깝게도 우리의 일반적인 기대와 컴퓨터의 현실에는 아주 큰 제약이 존재합니다.


1. 부동소수점 (Floating Point)의 배신

대부분의 프로그래밍 언어(파이썬 포함)는 소수점 계산을 할 때 부동소수점(float) 이라는 방식을 씁니다. 이 방식은 실생활의 측량에서는 굉장히 빠르고 편리하지만, 치명적인 약점이 있습니다. 컴퓨터는 데이터를 0과 1의 ‘이진수(Binary)’로만 이해할 수 있기 때문에, $10$진수의 평범한 소수를 컴퓨터의 2진수로 바꾸는 과정에서 할당된 메모리 공간(64비트)을 넘어서는 무한소수들을 끝에서 강제로 싹둑 자르고 반올림해버립니다.

즉, 완벽해야 할 비율의 세계에 미세한 오차(Error)가 발생하게 됩니다.

예를 들어, 우리가 계산기에 $0.1 + 0.2$를 치면 당연히 $0.3$이 나올 것이라고 확신합니다. 하지만 파이썬에게 이 계산을 시켜보면 충격적인 결과를 보게 됩니다.

# [Python] 0.1 + 0.2 는 정말로 0.3 이 맞을까?
a = 0.1
b = 0.2
result = a + b

print(f"0.1 + 0.2 = {result}")

if result == 0.3:
    print("계산이 완벽히 일치합니다!")
else:
    print("충격! 컴퓨터의 Float 한계로 인해 값이 일치하지 않습니다!")

[실행 결과]

0.1 + 0.2 = 0.30000000000000004
충격! 컴퓨터의 Float 한계로 인해 값이 일치하지 않습니다!

컴퓨터 메모리의 $64$비트 우편함으로는 무한의 세계를 완벽히 담아낼 수 없어서, 끝자리에 미세한 쓰레기값(0.000...04)이 달라붙었기 때문입니다. 이 오차를 무시하고 은행 우주선을 쏘아 올린다면 궤도가 틀어져 화성이 아닌 목성으로 날아가 버릴 것입니다!

0.1과 0.2의 Float 이진수 변환 과정에서 메모리 한계로 인해 발생하는 소수점 오차를 보여주는 SVG 메모리 그리드 시각화

2. 구원자: fractions 모듈 (완벽한 비율계산기)

이러한 부동소수점의 치명적인 오차를 막기 위해, 정말로 정밀한 수학/과학 계산이 필요한 프로그래머들은 소수(Float)를 쓰지 않습니다. 대신 유리수의 원형 형태(분자/분모)를 메모리에 고스란히 저장하는 특별한 도구를 씁니다.

파이썬에서는 fractions.Fraction이라는 마법사가 그 역할을 합니다.

# [Python] 오차율 0% 의 절대 계산: 분수(Fraction) 모듈
from fractions import Fraction

# 소수점(0.1, 0.2)을 쓰지 않고 가장 순수한 분수 형태로 변수를 만들겠습니다.
a = Fraction(1, 10)  # 1/10 (0.1)
b = Fraction(2, 10)  # 2/10 (0.2)
target = Fraction(3, 10) # 3/10 (0.3)

result = a + b

print(f"{a} + {b} = {result}")

if result == target:
    print("축하합니다! 오차율 0%로 완벽하게 3/10 과 일치합니다!")
else:
    print("계산 실패...")

[실행 결과]

1/10 + 1/5 = 3/10
축하합니다! 오차율 0%로 완벽하게 3/10 과 일치합니다!

(파이썬은 내부적으로 2/10을 가장 가벼운 기약분수인 1/5로 알아서 스위칭하여 계산해주었습니다.) 소수점 오류 없이 계산을 통분하여 3/10이라는 완벽한 유리수로 결과를 반환했습니다.

마무리

유리수는 “정수와 정수의 완벽한 비율”이라는 아름다운 개념에서 출발했지만, 현실 세계(컴퓨터)에서는 메모리의 한계 때문에 Float 자료형이 어쩔 수 없이 무한을 유한으로 깎아내며 오차를 만듭니다. 우리는 수학적 이상($a/b$ 비율)과 컴퓨터 공학적 현실(메모리 바이트 한계)의 차이를 이해하고, 상황에 맞는 가장 완벽한 코드를 짜야만 합니다.

다음 장부터는 이 유리수의 한계마저도 훌쩍 뛰어넘는, 수직선의 텅 빈 공간을 가득 메우는 촘촘한 수인 “실수(Real Numbers)”와 억울하게 바다에 빠져 죽은 사나이, 히파소스의 이야기를 다루어보겠습니다!

서브목차