https://docs.python.org/3/tutorial/floatingpoint.html#floating-point-arithmetic-issues-and-limitations

표시 오차

  • 문제: 0.1 같은 값이 “0.10000000000000000555…”로 저장되어 출력과 실제 값이 다름.
  • 대안: format()/str.format()으로 표시 자릿수를 통제합니다.
import math
print(format(math.pi, '.4f'))  # 3.1416

정확한 10진 계산

  • 문제: 금융/세금 계산에서 이진 부동소수 오차가 허용되지 않음.
  • 대안: decimal.Decimal로 문자열 입력을 사용해 10진 정밀도를 보장합니다.
from decimal import Decimal, getcontext
getcontext().prec = 10
price = Decimal('0.70') * Decimal('1.05')
print(price)  # 0.7350

유리수 표현

  • 문제: 1/3 같은 반복소수가 정확히 표현되지 않음.
  • 대안: fractions.Fraction으로 분수 기반 연산을 합니다.
from fractions import Fraction
frac = Fraction(1, 3)
print(frac * 3 == 1)  # True

부정확한 비교

  • 문제: 0.1 + 0.1 + 0.1 == 0.3이 False.
  • 대안: math.isclose()나 반올림된 값을 비교합니다.
import math
print(math.isclose(0.1 * 3, 0.3))  # True

누적 합 오차

  • 문제: 많은 부동소수를 더할 때 오차가 쌓임.
  • 대안 1: sum()은 확장 정밀도로 중간 오차를 줄입니다.
  • 대안 2: math.fsum()은 더 정확한 한 번의 반올림만 수행합니다.
import math
arr = [0.1] * 10
print(sum(arr))        # 0.9999999999999999
print(math.fsum(arr))  # 1.0

내부 값 확인

  • 문제: 실제 저장된 비율을 알고 싶을 때.
  • 대안: float.as_integer_ratio()나 float.hex()로 완전한 표현을 확인하고 재생성합니다.
x = 0.1
num, den = x.as_integer_ratio()  # (3602879701896397, 36028797018963968)
restored = num / den
print(restored == x)  # True

이진 부동소수 특징

  • 대부분의 10진 소수는 2진수로 정확히 표현되지 않아 0.1 같은 값도 내부에선 3602879701896397 / 2**55 같은 근사치로 저장됩니다.
  • Python REPL이 0.1을 보여도 실제 메모리 값은 0.10000000000000000555…입니다.
  • 똑같은 근사치를 공유하는 10진 값들이 많아, Python 3.1 이후엔 가장 짧은 표현(0.1)만 보여 주지만 repr(x)가 eval(repr(x))== x를 만족하도록 설계돼 있습니다.

실수 비교/출력 주의

  • 0.1 + 0.1 + 0.1 == 0.3처럼 단순 비교가 False가 될 수 있으므로 math.isclose()나 반올림된 값을 비교하세요.
  • format()이나 str.format()으로 필요한 자리수만 표시하면 보기 좋습니다. 예: format(math.pi, ‘.2f’) → ‘3.14’.

정밀도가 더 필요한 경우

  • 법규/회계 수준의 정확도가 필요하면 decimal.Decimal을 사용하세요. 문자열 입력으로 10진수를 그대로 표현하며 자릿수·반올림 규칙을 제어할 수 있습니다.
  • 분수 기반 정확도가 필요하면 fractions.Fraction을 쓰면 1/3 같은 값도 정확히 표현됩니다.
  • 대규모 수치 연산은 NumPy/SciPy가 제공하는 고급 부동소수 처리·수학 함수를 활용하세요.

내부 값 확인 도구

  • float.as_integer_ratio() → 예: (0.1).as_integer_ratio()는 (3602879701896397, 36028797018963968)을 반환해 원래 비율을 알려 줍니다.
  • float.hex()와 float.fromhex()를 써서 플랫폼 간 동일한 값을 정확히 교환할 수 있습니다.

정확한 합계 계산

  • sum()은 확장 정밀도를 이용해 부분 합의 오차를 줄입니다. math.fsum()은 더 느리지만 “잃어버린 자릿수”를 추적하여 한 번만 반올림하므로 극단적인 경우에도 정확도가 우수합니다.
  • 예: sum([0.1] * 10) 1.0은 True지만 0.1 + ... + 0.1 1.0은 False입니다.

대표적 오차 사례 계산

  • Fraction.from_float(0.1) → Fraction(3602879701896397, 36028797018963968)
  • Decimal.from_float(0.1) → Decimal(‘0.1000000000000000055511151231257827021181583404541015625’)

결론: 부동소수 오차는 하드웨어 특성이므로 버그가 아니며, 표시를 반올림하거나 decimal/fractions 같은 정확한 대안을 선택하면 대부분의 문제를 해결할 수 있습니다.

← python 3.14으로