2025. 2. 8. 18:02ㆍ소프트웨어/기초
부동 소수점 연산의 한계
부동 소수점 연산은 우리가 예상하는 정확한 값과 다른 결과를 반환할 수 있습니다.
예를 들어, 다음과 같은 연산을 수행할 때:
print(0.1 + 0.2) # 예상: 0.3, 실제 출력: 0.30000000000000004
우리는 0.1 + 0.2 = 0.3이 나와야 한다고 생각하지만, 실제 출력값은 0.30000000000000004입니다.
이러한 오차는 부동 소수점 방식이 실수를 근사값으로 저장하기 때문에 발생합니다.
1. 왜 부동 소수점 연산이 정확하지 않을까?
(1) 일부 10진 소수는 2진수로 정확히 표현할 수 없음
컴퓨터는 실수를 **2진법(0과 1)**으로 저장하는데, 일부 10진수는 2진법으로 정확하게 변환할 수 없습니다.
예를 들어, 10진수 0.1을 2진수로 변환하면 무한 반복되는 소수가 됩니다.
- 10진법에서는 0.1을 정확하게 저장할 수 있지만,
- 2진법에서는 끝없이 반복되므로, 컴퓨터는 일정한 비트 수에서 근사값으로 저장하게 됩니다.
이 때문에 0.1, 0.2 등의 숫자는 정확하게 표현되지 않고, 연산 과정에서 오차가 발생합니다.
(2) IEEE 754 부동 소수점 표준의 제한
부동 소수점 숫자는 IEEE 754 표준을 따르며, 실수를 다음과 같이 저장합니다.
- 32비트(단정밀도)에서는 23비트의 가수(Mantissa)만 사용할 수 있습니다.
- 64비트(배정밀도)에서는 52비트의 가수(Mantissa)만 사용할 수 있습니다.
- 따라서, 일부 소수는 주어진 비트 안에서 정확히 저장할 수 없으며, 근사값으로 저장됨
(3) 부동 소수점 연산 과정에서 발생하는 누적 오차
부동 소수점 숫자는 단순한 덧셈, 뺄셈뿐만 아니라 곱셈, 나눗셈에서도 오차가 누적될 수 있습니다.
예제 1: 작은 값이 반복적으로 더해질 때 누적되는 오차
result = 0.0
for _ in range(100):
result += 0.1
print(result) # 예상: 10.0, 실제 출력: 9.99999999999998
🔹 원인: 0.1이 정확하게 표현되지 않기 때문에, 반복적으로 더하면 오차가 누적됨.
예제 2: 복잡한 연산에서 오차가 증폭되는 경우
import math
# π 값을 이용한 원둘레 계산
radius = 1.1
circumference = 2 * math.pi * radius
print(circumference) # 예상: 6.9115..., 실제 출력: 6.911503837897545
🔹 원인: math.pi 자체가 IEEE 754 형식으로 저장된 근사값이기 때문에, 연산을 거듭할수록 오차가 누적됨.
2. IEEE 754의 특수한 값들
IEEE 754 표준에서는 특수한 값들을 정의하여, 연산 중 예외적인 상황을 처리할 수 있도록 설계되어 있습니다.
특수 값 | 설명 | 예제 연산 | Python 출력값 |
Infinity (무한대, ∞) | 0으로 나누거나 매우 큰 숫자를 표현할 때 발생 | 1.0 / 0.0 | inf |
-Infinity (-∞) | 음의 무한대 | -1.0 / 0.0 | -inf |
NaN (Not a Number) | 연산이 정의되지 않은 경우 | 0.0 / 0.0 | nan |
예제 3: IEEE 754 특수 값 확인
import math
# 무한대 연산
print(1.0 / 0.0) # inf
print(-1.0 / 0.0) # -inf
# NaN 연산
print(math.sqrt(-1)) # nan
print(0.0 / 0.0) # nan
🔹 주의: NaN은 어떤 숫자와 비교해도 False를 반환합니다.
print(float('nan') == float('nan')) # False
NaN은 자기 자신과도 같지 않음!
3. 부동 소수점 연산 오차를 해결하는 방법
(1) 반올림(round) 사용
작은 오차를 없애기 위해 round() 함수를 사용할 수 있습니다.
print(round(0.1 + 0.2, 10)) # 출력: 0.3
하지만, 이 방법은 단순히 출력값을 조정하는 것일 뿐, 근본적인 문제를 해결하지는 않습니다.
(2) decimal 모듈 사용 (정확한 소수 연산)
decimal 모듈을 사용하면 10진법 그대로 연산할 수 있어, 부동 소수점 오차를 방지할 수 있습니다.
from decimal import Decimal
a = Decimal('0.1')
b = Decimal('0.2')
print(a + b) # 출력: 0.3 (정확한 값)
- decimal.Decimal을 사용하면 0.1과 0.2가 정확하게 저장되므로 연산 결과도 정확합니다.
- 금융 계산 등 정확한 소수점 연산이 필요한 경우에 사용됩니다.
(3) fractions 모듈 사용 (분수 연산)
부동 소수점 대신 분수(Fraction)로 연산하면 정확도를 유지할 수 있습니다.
from fractions import Fraction
a = Fraction(1, 10) # 1/10 (0.1)
b = Fraction(2, 10) # 2/10 (0.2)
print(a + b) # 출력: 3/10
- 분수 형태로 저장하여 부동 소수점 오차 없이 정확한 계산이 가능합니다.
- 하지만 연산 속도가 느릴 수 있으므로, 특정 경우에만 사용하는 것이 좋습니다.
4. 정리
✅ 부동 소수점 연산은 정확하지 않으며, 일부 숫자는 2진법으로 정확히 표현할 수 없다.
✅ 0.1 같은 숫자는 2진수로 변환 시 무한 반복되므로, 컴퓨터는 근사값으로 저장한다.
✅ 부동 소수점 연산 과정에서 오차가 누적될 수 있으며, 금융 계산 등에는 적절하지 않다.
✅ IEEE 754에는 특수한 값(NaN, Infinity 등)이 있으며, 예외적인 연산 처리를 가능하게 한다.
✅ 오차를 해결하기 위해 round(), decimal 또는 fractions 모듈을 활용할 수 있다.
'소프트웨어 > 기초' 카테고리의 다른 글
부동 소수점 - 6. 정리 및 실전 예제 (부동 소수점 & IEEE 754 표준) (0) | 2025.02.08 |
---|---|
부동 소수점 - 5. 정확도가 중요한 경우 어떻게 해결할까? (0) | 2025.02.08 |
부동 소수점 - 3. 부동 소수점 표기법 (IEEE 754 표준 개념) (0) | 2025.02.08 |
부동 소수점 - 2. 컴퓨터가 숫자를 표현하는 방식 (0) | 2025.02.08 |
부동 소수점 - 1. 부동 소수점이란? (0) | 2025.02.08 |