언어/MySQL

[MySQL] 소수점이 있는 금액의 타입은 float? float, double 절대 금지! decimal 또는 string로 지정하자!

보리시스템 2024. 4. 2.

글로벌 서비스에서 환전한 금액이 아닌 해당 통화의 금액으로 데이터를 저장해야 하는 상황이 생겨 소수점이 있는 금액 데이터에 대해 고려하며 알게 된 사실이다.

 

소수점이면 float나 double이겠지라고 생각하고 DB 칼럼의 타입을 지정했다가는 근사치 값으로 연산이 이뤄지는 불상사가 일어날 수 있다. 아래 사진에서와 같이 MySQL 문서에서도 아예 제목 옆에 'Approximate Value(근사값)'라고 적고 있다.

[출처] https://dev.mysql.com/doc/refman/8.3/en/floating-point-types.html

 

보다 직관적으로 말하지면 부동소수점(float, double)의 경우 아래와 같이 저장되고 출력된다.

1. FLOAT에 16777217을 입력하면 16777216이 저장되고, 1.677722e+07이 출력된다(정밀도가 생략되면, 7개의 유효 숫자로 표현하므로 8번째 숫자를 반올림함).

2. FLOAT(5)에 16777217을 입력하면 16777216이 저장되고, 1.677722e+07이 출력된다(정밀도가 7 이하이면, 7개의 유효 숫자로 표현하므로 8번째 숫자를 반올림함).

3. FLOAT(5)에 16777.217을 입력하면 16777.216이 저장되고, 1.677722e+04가 출력된다(정밀도가 7 이하이면, 7개의 유효 숫자로 표현하므로 8번째 숫자를 반올림함).

4. FLOAT(10)에 16777.217를 지정하면 16777.217이 저장되고, 1.677721700000000e+04가 출력된다(정밀도가 7보다 크고 38 이하이면, DOUBLE 타입으로 변환되어 15개의 유효 숫자로 표현하므로 0을 채움)

[출처] https://www.cubrid.org/manual/ko/9.1.0/sql/datatype.html#float-real

 

고정 소수점인 decimal과 비교해서 설명하면 바로 이해가 된다.

float, double 등 부동 소수점으로 저장하면 해당 숫자 데이터를 이진수로 변환한 뒤에 통으로 바이트에 저장한다. 예를 들어 십진수를 이진수로 변환하는 과정에서 순환소수가 발생한 상황이라고 하면 지원하는 비트 크기 안에서 꼬리를 잘라버린다. 이로 인해 오차가 발생하는 것이다.

하지만 고정 소수점인 decimal의 경우 숫자를 자릿수 각각으로 저장한다. 예를 들어 123이라는 숫자에 대해 1과 2와 3으로 각각 저장한다.

[출처] https://woonys.tistory.com/279

 

이러한 이유로 고정소수점인 Decimal을 사용하는 것이 최선의 방법이겠다. 해외 PG사의 API 가이드 문서를 보다 보면 금액의 타입을 string으로 지정하는 경우도 있지만 타입 관리가 중요하다면 Decimal이 좋겠다.