배경
숫자 계산에서 가장 중요한 건 "정확성"입니다.
특히 금융, 결제, 세금, 환율 계산처럼 단 1원의 오차도 허용되지 않는 분야에서는 Double과 같은 부동 소수점 타입이 적합하지 않습니다.
Swift에서 흔히 사용되는 숫자 타입에는 Double, Decimal, Int64가 있으며, 각각의 특성과 성능, 정확성은 크게 다릅니다.
이 글에서는 왜 Decimal을 써야 하는지를 Double, Int64와 비교하며 설명하고자 합니다.
Double의 문제점
Swift에서 가장 많이 사용되는 실수형 타입은 Double입니다.
Double은 64비트 이진 부동 소수점 방식(IEEE 754 표준)을 따르며, 넓은 범위의 수를 표현할 수 있고 연산 속도도 빠릅니다.
하지만 정확한 소수값을 표현해야 하는 상황, 예를 들어 금액, 세금, 이자율 계산 등에서는 예상치 못한 결과가 발생할 수 있습니다.
print(0.1 + 0.2) // 출력: 0.30000000000000004
print(0.1 + 0.2 == 0.3) // false
이유는 간단합니다.
10진수의 일부 값은 2진수로 정확히 표현할 수 없기 때문입니다.
결과적으로 정산 결과가 어긋나거나, == 비교가 실패하는 등의 문제가 발생합니다.
부동 소수점(Floating-Point)란?
컴퓨터는 모든 수를 0과 1, 즉 이진수(binary) 로 저장합니다.
Double 타입도 예외는 아니며, 실수를 표현하기 위해 IEEE 754 이진 부동 소수점(floating-point) 방식을 사용합니다.
이 방식은 하나의 숫자를 다음의 세 가지 구성 요소로 나누어 저장합니다:
구성 | 요소 | 비트 수 |
부호(Sign) | 양수(0) / 음수(1) | 1비트 |
지수부(Exponent) | 소수점 위치 조절 (2의 몇 승인지) | 11비트 |
가수부(Significand, 또는 Fraction) | 유효 숫자 (정밀도) | 52비트 |
사람은 0.1을 "1/10"이라고 생각하지만, 컴퓨터는 이를 2진수로 다음처럼 표현합니다.
0.1 (10진수) = 0.0001100110011001100... (2진수, 무한 반복)
그러나 Double은 유한한 52비트만 사용해서 이 수를 저장해야 하므로, 이 무한 반복을 중간에 자르고 근사값으로 저장합니다.
이것이 바로 0.1 + 0.2 == 0.3이 실패하는 이유입니다.
0.1과 0.2는 둘 다 근사값이고, 이 둘을 더한 결과도 또 다른 근사값이기 때문입니다.
Decimal과 고정 소수점(Fixed-Point)
Double이 속도와 범위 면에서는 뛰어나지만, 정밀한 10진수 소수 표현에서는 한계를 갖습니다.
이러한 문제를 해결하기 위해 등장한 방식이 바로 고정 소수점(Fixed-Point)이며, Swift에서는 이를 Decimal 타입이 구현하고 있습니다.
고정 소수점이란?
고정 소수점 방식은 이름 그대로 소수점의 위치를 고정한 상태로 수를 표현하는 방식입니다. 핵심은 다음과 같습니다.
실수(소수 포함)를 정수처럼 저장하고, 소수점의 위치만 따로 기억하거나 미리 정해놓는 방식
예를 들어 10.35 같은 값을 저장한다고 가정해봅시다.
- 소수 둘째 자리까지 고정된 방식이라면,
- 10.35는 1035라는 정수로 저장됩니다.
- 소수점은 항상 둘째 자리에 있다고 암묵적으로 가정하거나, scale 값으로 따로 저장합니다.
- 최종적으로 1035 → 10.35로 해석됩니다.
이 방식은 이진 부동 소수점처럼 소수점 위치가 이동하지 않고,
정확한 소수 표현이 가능하다는 점이 가장 큰 장점입니다.
Swift의 Decimal 타입
Swift의 Decimal 타입은 Apple의 NSDecimal 기반으로,
고정 소수점 방식의 128비트 십진 실수 타입입니다.
- 내부적으로는 정수 값을 저장하고,
- 소수점의 위치(scale)를 따로 보관합니다.
- 이 방식 덕분에 0.1, 0.3과 같은 10진수 소수도 정확하게 표현할 수 있습니다.
let a = Decimal(string: "0.1")!
let b = Decimal(string: "0.2")!
print(a + b) // 출력: 0.3 (정확하게 출력됨)
이 결과는 Double에서의 0.1 + 0.2와 비교하면 매우 명확한 차이를 보여줍니다.
Decimal vs Double 비교
항목 | Double | Decimal |
표현 방식 | 이진 부동 소수점 (IEEE 754) | 십진 고정 소수점 |
정밀도 | ❌ 오차 있음 (근사값) | ✅ 정확한 표현 |
0.1 + 0.2 | 0.30000000000000004 | 0.3 |
속도 | 빠름 | 느림 (정수 기반 연산) |
메모리 | 64비트 | 128비트 |
사용 예 | UI 애니메이션, 과학 계산 | 💰 금융, 세금, 환율 계산 등 |
Decimal의 장점은 다음과 같습니다.
- 정확한 소수 표현: 사람이 기대한 값과 계산 결과가 일치합니다.
- 소수점 오차 없음: 0.1, 0.3 같은 값도 완벽하게 표현됩니다.
- 의미 있는 비교 가능: Decimal(0.1) + Decimal(0.2) == Decimal(0.3) → true
하지만 다음과 같은 단점도 존재할 수 있습니다.
- 연산 속도가 Double보다 현저히 느릴 수 있습니다.
- 메모리 사용량도 2배 이상 (64bit → 128bit)
- CPU에 따라 계산 비용이 비쌀 수 있음
하지만 숫자의 정확성이 중요한 도메인에서는 Decimal을 통해 값을 정확하게 전달하는 것이 더욱 중요할 수 있기 때문에 적절한 상황에 따라 고려해야 합니다. 즉, 금액 계산, 세금, 환율 등 금융과 관련된 도메인에서는 Decimal을 사용해야 할 수도 있습니다.
정리
- Decimal은 십진 고정 소수점 방식으로 소수점을 정수로 변환해 저장하며, 별도의 스케일 정보로 소수점 위치를 기억합니다.
- 덕분에 오차 없는 10진수 계산이 가능하며, 금융/회계/정산 등 정확한 금액 계산이 필요한 도메인에서 필수적으로 사용됩니다.
- 반면 Double은 빠르고 범위가 넓지만, 0.1, 0.3 같은 값을 정확히 표현하지 못하기 때문에, 비교 연산이나 누적 연산에서 문제가 발생할 수 있습니다.
결론적으로 속도가 필요한 곳에는 Double, 정확성이 필요한 금융 도메인 같은 곳에는 Decimal 선택을 고려해보세요!
'iOS' 카테고리의 다른 글
[iOS] Optimistic UI의 정의 및 적용 사례 (SwiftUI) (2) | 2025.06.08 |
---|---|
[iOS] Xcode의 빌드 과정 (0) | 2025.01.16 |
[UIKit] UILabel.text와 문자열 보간법의 옵셔널 표현의 차이 (0) | 2025.01.03 |
[iOS] SwiftLint 설치 및 적용하기 (0) | 2023.12.23 |
[iOS] 협업 시 하나의 Bundle Identifier로 설정하기 (Provisioning Profile, Certificate 공유) (0) | 2023.11.21 |