Post

개발자라면 Decimal과 부동소수점은 알아야지?

부동소수점 함정에 주의

금융 애플리케이션 개발 시, 정확한 계산 로직 구현은 필수 불가결하다. 부동소수점 자료형(Float, Double)은 근사치 계산으로 인해 금융 계산에 적합하지 않다. Decimal을 사용하여 이 문제를 해결하고 정확한 계산을 보장해야 한다.

부동소수점 함정: IEEE 754 표준의 한계

대부분의 프로그래밍 언어는 부동소수점 표현에 IEEE 754 표준을 따른다. 64비트 double precision은 부호(1비트), 지수(11비트), 가수(52비트)로 구성된다.

32비트 single precision은 64비트에 비해 표현 가능한 비트수가 절반이므로 정밀도가 상대적으로 떨어진다.

예를 들어 0.1을 double precision 부동소수점으로 표현하면 다음과 같다.

  • 부호: 0 (양수)
  • 지수: 01111111011 (2진법) (-4 + 1023 bias = 1019)
  • 가수: 1001100110011001100110011001100110011001100110011010 (2진법)

따라서 0.1은 64비트 double precision 부동소수점으로 다음과 같이 표현된다.

1
0 01111111011 1001100110011001100110011001100110011001100110011010

문제는 0.1을 2진법으로 정확하게 표현할 수 없다는 점이다. 2진법으로는 1/10을 무한 소수로 표현해야 하므로, 컴퓨터는 이를 유한한 자릿수로 근사하여 저장한다. 이 과정에서 필연적으로 오차가 발생하며, 이는 금융 계산에 치명적인 오류를 야기할 수 있다.

1
2
val num = 0.1 + 0.2
println(num)  // 0.30000000000000004

결과값은 0.3이 아니라 0.30000000000000004이다. 이러한 사소한 오차가 금융 로직에서 큰 문제를 일으킬 수 있다.

Decimal: 정확한 금융 계산의 해결책

Java 언어에서 BigDecimal은 문자열 형태의 10진수 값과 스케일(소수점 이하 자릿수)을 저장한다. 내부적으로는 BigInteger를 사용하여 정수부를 표현하고, 스케일 정보를 통해 소수점 위치를 관리한다. 이러한 구조 덕분에 BigDecimal은 0.1, 0.2와 같은 숫자를 정확하게 표현하고 연산할 수 있다. 물론 장점만 있는 것이 아니다. 단점도 간단하게 말하자면 성능이 IEEE 754 보다 상대적으로 떨어진다는 점이다. 하지만 2024년 현재, 컴퓨팅 리소스는 굉장히 발전했다. 또한 컴퓨터 리소스를 쉽게 제공하는 클라우드 업체도 많다. 그래서 여러 장단점을 따지며 사용하면 좋겠다.

1
2
3
4
5
6
import java.math.BigDecimal

val num1 = BigDecimal("0.1")
val num2 = BigDecimal("0.2")
val result = num1 + num2
println(result)  // 0.3

위 예제코드는 코틀린 코드이다.

결론

금융 앱 개발 시, 정확한 계산 로직 구현은 필수다. 부동소수점 함정에 빠지지 말고 Decimal을 사용하여 안전하고 정확한 금융 서비스를 구현해야 한다.

This post is licensed under CC BY 4.0 by the author.