왜 0.1 + 0.2가 코드에서 0.3이 되지 않을까

발행: (2026년 2월 13일 오후 12:22 GMT+9)
13 분 소요
원문: Dev.to

Source: Dev.to

print(0.1 + 0.2)

당신은 0.3이 출력되길 기대합니다. 하지만 실제로는 0.30000000000000004가 나옵니다.

계산기에서는 0.3이라고 표시됩니다. 엑셀에서도 0.3이라고 나옵니다. 당신의 뇌도 0.3이라고 생각합니다. 하지만 코드에서는 그렇지 않죠. 그리고 이것은 파이썬 버그가 아니라—모든 프로그래밍 언어에서 동일하게 발생합니다. JavaScript, Java, C++, Ruby, Go—모두 같은 방식으로 기본적인 산술을 배신합니다.

이처럼 사소해 보이는 특이 현상은 수백만 달러 규모의 재앙을 초래했습니다. 1991년, Patriot 미사일 방어 시스템이 부동소수점 오차가 누적돼 이라크 스커드 미사일을 요격하지 못했고, 그 결과 28명의 군인이 사망했습니다. 금융 시스템은 통화 계산에서 발생하는 반올림 오류로 매일 수천 달러를 잃고 있습니다. 과학 시뮬레이션은 엉터리 결과를 만들어냅니다. 의료 투여 소프트웨어는 잘못된 계산을 합니다.

그럼에도 대부분의 개발자는 이런 일이 발생하는지, 어떻게 해결해야 하는지를 배우지 못합니다. 그들은 그냥 무작위로 반올림 함수를 적용해 상황이 맞아 보일 때까지 계속합니다. 오늘은 그 문제를 해결해 봅시다.

실제 문제

컴퓨터는 실제로 여러분이 생각하는 방식대로 십진수를 저장하지 않습니다. 코드에 0.1을 쓰면, 컴퓨터는 이를 이진수—0과 1—로 변환합니다. 그런데 핵심은 이렇습니다: 대부분의 십진 소수는 이진수로 정확히 표현될 수 없습니다.

예를 들어 1/3을 생각해 보세요. 십진수로는 0.333…을 무한히 씁니다. 몇 개의 3을 써도 정확한 값을 잡을 수 없습니다. 이진수도 소수점 이하 10진수와 같은 문제를 가지고 있기 때문에 0.1은 다음과 같이 됩니다.

0.0001100110011001100110011…   (무한히 반복)

컴퓨터는 메모리가 한정되어 있기 때문에 그 무한한 시퀀스를 잘라냅니다. 잘린 버전은 0.1에 가깝지만 정확하지는 않습니다. 두 개의 근사값을 더하면, 그 작은 오차들이 합쳐져 눈에 띄게 됩니다.

이것은 모든 십진수 연산에 영향을 미칩니다. 곱셈, 나눗셈, 뺄셈—모두 이 근본적인 근사값을 물려받습니다. 오차는 각 연산마다 누적됩니다. 루프 안에서 백만 번 계산을 수행하면, 결과는 진실과 크게 차이가 날 수 있습니다.

왜 아무도 경고하지 않았는가

  • 학습의 사각지대 – 초보자는 정수와 문자열을 먼저 배우기 때문에 부동소수점 연산을 접했을 때 일반 수학처럼 동작한다고 가정한다.
  • 희박한 다루기 – 컴퓨터‑과학 강의에서는 IEEE‑754 부동소수점 표준을 대충 언급하지만 실제 세계에서의 영향을 자세히 설명하지 않는다. 많은 졸업생들은 금융 소프트웨어가 돈을 정수로 처리하는 이유를 배우지 못한다.
  • 숨겨진 증상 – 대부분의 언어는 부동소수점 값을 자동으로 반올림해 표시하므로 0.30000000000000004 가 로그에서는 종종 0.3 으로 나타난다. 진짜 문제는 동등성 검사가 신비롭게 실패하거나 정밀도가 중요한 경우에야 드러난다.
  • 소규모에서는 보이지 않음 – 몇 개의 숫자를 더하는 것은 문제 없지만, 대규모 데이터셋, 금융 계산, 혹은 작은 오차가 폭발하는 반복 알고리즘을 다룰 때 오류가 눈에 보인다.

Source:

실제로 작동하는 해결책

1. 부동소수점 값을 정확히 비교하지 말 것

x가 부동소수점 연산에서 나온 값이라면 if x == 0.3과 같이 정확히 비교하지 마세요. 대신 충분히 가까운지 확인합니다:

def float_equal(a, b, tolerance=1e-9):
    """Return True if a and b differ by less than tolerance."""
    return abs(a - b) < tolerance

result = 0.1 + 0.2
if float_equal(result, 0.3):
    print("Close enough!")

허용 오차(보통 epsilon이라고 함)는 허용 가능한 오류 범위를 정의합니다. 대부분의 경우, 10억분의 1(1e-9)이면 충분합니다; 정확도 요구에 따라 조정하세요.

2. 금액은 정수형으로 다룰 것

통화에 부동소수점을 절대 사용하지 마세요. 가장 작은 단위(센트, 파이시 등)를 정수로 저장합니다:

price_cents = 1999                # $19.99
tax_cents   = int(price_cents * 0.07)  # 7 % tax
total_cents = price_cents + tax_cents
print(f"Total: ${total_cents / 100:.2f}")

모든 연산이 정확하게 이루어지며, 표시할 때만 소수 형태로 변환합니다.

3. 정확한 십진 연산을 위해 decimal 라이브러리 사용

진정한 십진 정밀도가 필요할 때(회계, 과학 측정, 법률 문서 등) decimal 타입을 사용합니다. 파이썬에는 내장된 것이 있습니다:

from decimal import Decimal

a = Decimal('0.1')
b = Decimal('0.2')
print(a + b)          # Exactly 0.3

중요: 숫자는 문자열로 전달하세요. Decimal(0.1)은 먼저 부동소수점(0.10000000000000000555…)을 만들고 그 오류를 그대로 이어받습니다.

4. 필요할 때 정밀도 높이기

파이썬의 float는 64비트 double이며, 약 15‑17자리 십진 정확도를 제공합니다. 이것으로 부족하면 decimal 모듈을 이용해 임의의 정밀도를 설정할 수 있습니다:

from decimal import Decimal, getcontext

getcontext().prec = 50          # 50 decimal places
result = Decimal(1) / Decimal(3)
print(result)   # 0.33333333333333333333333333333333333333333333333333

정밀도를 높이면 계산 속도가 느려지지만, 때로는 정확성이 속도보다 중요합니다.

정밀도 문제가 발생할 때

Financial systems are obvious victims, but the danger extends further:

  • Scientific simulations – Millions of iterations can drift completely off course.
  • Machine‑learning pipelines – Accumulated rounding errors degrade model accuracy.
  • Game physics engines – Ignoring float precision creates glitches where objects phase through walls.
  • GPS, weather forecasting, cryptocurrency exchanges, medical equipment – Small sensor‑reading errors become catastrophic when propagated through calculations.

A famous cautionary tale is the Vancouver Stock Exchange index. Launched in 1982 at 1000.00 points, it inexplicably fell to 524.811 after 22 months—far from the expected ~1100—due to accumulated floating‑point rounding errors.

요점

Floating‑point representation is an approximation, not a bug. Understanding its limits and applying the right tools—tolerant comparisons, integer arithmetic for money, decimal libraries, or higher‑precision types—keeps your programs accurate and reliable.

생존 전략

1. 정밀도가 중요한 경우를 파악하라

  • 블로그 게시물 조회수? 부동소수점이면 충분합니다.
  • 은행 계좌 잔액? 절대 아니오.
  • 오류를 전혀 허용할 수 없는 경우—금융 거래, 법률 문서, 생명에 중요한 시스템—처음부터 정확한 연산을 사용하세요.

2. 경계 사례를 테스트하라

  • 반복 소수가 포함된 계산을 특별히 확인하는 단위 테스트를 작성하세요.
  • 0.1, 0.01, 0.001 같은 값을 함수에 입력해 보세요.
  • 많은 버그는 $10.10 같은 금액을 입력할 때까지 드러나지 않습니다(반올림된 정수가 아니라).

3. 도구를 이해하라

  • 사용 중인 언어가 제공하는 decimal 또는 bignum 라이브러리의 문서를 읽으세요.
  • 라이브러리마다 특성이 있습니다: 일부는 정밀도 제한이 있고, 다른 일부는 연산에 따라 동작이 다릅니다.
  • 해결책을 선택하기 전에 이러한 세부 사항을 파악하세요.

4. 누적 오류에 주의하라

  • 루프에서 수천 개의 값을 합산할 때 작은 오류가 쌓입니다.
  • 정확한 합산을 위해 설계된 알고리즘, 예를 들어 Kahan 합산처럼 손실된 정밀도를 추적하고 보정하는 방법을 고려하세요.

5. 결정 사항을 문서화하라

  • 금액에 정수를 사용하거나 정밀도에 decimal을 사용할 때, 그렇게 했는지 설명하는 주석을 남기세요.
  • 미래의 당신이나 다른 개발자가 부동소수점으로 “단순화”하려 할 때, 그 이유를 이해하고 감사하게 될 것입니다.

요약

컴퓨터는 소수에 대해 거짓말을 합니다. 항상 그랬고 앞으로도 그럴 겁니다. 이것은 버그가 아니라 하드웨어 수준에서 이진 연산이 작동하는 방식입니다.

부동소수점이 정확한 값이 아니라 근사값이라는 것을 받아들이면, 더 탄탄한 코드를 작성할 수 있습니다.

  • 다음에 이상한 계산 결과를 보게 되면, 코드가 고장 났다고 가정하기 전에 부동소수점 정밀도가 실제 원인인지 확인하세요.
  • 그런 다음 적절한 도구를 사용하세요: epsilon 비교, 정수 연산, 혹은 decimal 라이브러리.

당신의 금융 소프트웨어는 균형을 맞출 것이고, 과학 시뮬레이션은 수렴할 것입니다. 그리고 0.1 + 0.2가 초등학교에서 배운 모든 것을 배신한다는 것을 다시는 의심하지 않을 겁니다.

0 조회
Back to Blog

관련 글

더 보기 »

dev 여정을 시작하기

배경: 나는 초보자이지만 완전히 초보자는 아니다. 나는 80년대에 어린 시절 BASIC을 배웠고, 20대에 HTML + CSS를 배웠지만, 그 이후로는 코딩을 거의 하지 않았다.