Decimal 데이터 타입

Decimal 데이터 타입

덧셈·뺄셈·곱셈 연산 중에도 정밀도를 유지하는 부호 있는 고정소수점 숫자 타입이에요. 나눗셈에서는 최하위 자릿수가 버려져요(반올림되지 않아요).

출처: 문서

본문

덧셈(add)·뺄셈(subtract)·곱셈(multiply) 연산 중에 정밀도를 유지하는 부호 있는 고정소수점 숫자예요. 나눗셈(division)에서는 최하위 자릿수가 버려져요(반올림되지 않아요).

매개변수 (Parameters)

  • P — 정밀도. 유효 범위는 [1 : 76]. 숫자가 가질 수 있는 십진 자릿수(소수 포함)를 결정해요. 기본 정밀도는 10이에요.

  • S — 스케일(scale). 유효 범위는 [0 : P]. 소수가 가질 수 있는 십진 자릿수를 결정해요.

Decimal(P)Decimal(P, 0)과 동등해요. 마찬가지로 Decimal 문법은 Decimal(10, 0)과 동등해요. P 매개변수 값에 따라 Decimal(P, S)는 다음의 동의어예요.

  • P가 [1 : 9] — Decimal32(S)의 동의어
  • P가 [10 : 18] — Decimal64(S)의 동의어
  • P가 [19 : 38] — Decimal128(S)의 동의어
  • P가 [39 : 76] — Decimal256(S)의 동의어

Decimal 값 범위 (Decimal Value Ranges)

  • Decimal(P, S) — (-1 * 10^(P - S), 1 * 10^(P - S))
  • Decimal32(S) — (-1 * 10^(9 - S), 1 * 10^(9 - S))
  • Decimal64(S) — (-1 * 10^(18 - S), 1 * 10^(18 - S))
  • Decimal128(S) — (-1 * 10^(38 - S), 1 * 10^(38 - S))
  • Decimal256(S) — (-1 * 10^(76 - S), 1 * 10^(76 - S))

예를 들어 Decimal32(4)는 -99999.9999부터 99999.9999까지 0.0001 간격으로 수를 담을 수 있어요.

내부 표현 (Internal Representation)

내부적으로 데이터는 각각의 비트 폭을 가진 일반 부호 있는 정수로 표현돼요. 메모리에 저장할 수 있는 실제 값 범위는 위에서 명시한 것보다 조금 더 넓으며, 문자열에서 변환할 때만 검사돼요. 최신 CPU는 128비트·256비트 정수를 네이티브로 지원하지 않으므로 Decimal128과 Decimal256에 대한 연산은 에뮬레이션돼요. 그래서 Decimal128과 Decimal256은 Decimal32/Decimal64보다 훨씬 느리게 동작해요.

연산과 결과 타입 (Operations and Result Type)

Decimal에 대한 이항 연산은 (인자 순서와 무관하게) 더 넓은 결과 타입을 만들어내요.

  • Decimal64(S1) <op> Decimal32(S2) -> Decimal64(S)
  • Decimal128(S1) <op> Decimal32(S2) -> Decimal128(S)
  • Decimal128(S1) <op> Decimal64(S2) -> Decimal128(S)
  • Decimal256(S1) <op> Decimal<32|64|128>(S2) -> Decimal256(S)

스케일 규칙:

  • 덧셈, 뺄셈: S = max(S1, S2).
  • 곱셈: S = S1 + S2.
  • 나눗셈: S = S1.

Decimal과 정수 사이의 유사한 연산에서 결과는 인자와 같은 크기의 Decimal이에요. Decimal과 Float32/Float64 사이의 연산은 정의되지 않아요. 필요하다면 toDecimal32, toDecimal64, toDecimal128 또는 toFloat32, toFloat64 내장 함수로 인자 하나를 명시적으로 캐스팅할 수 있어요. 결과가 정밀도를 잃을 수 있고 타입 변환은 계산 비용이 큰 연산이라는 점을 명심하세요.

Decimal의 일부 함수는 결과를 Float64로 반환해요(예: var, stddev). 중간 계산은 여전히 Decimal로 수행될 수 있는데, 이는 값이 같더라도 Float64 입력과 Decimal 입력 사이에 결과가 달라질 수 있음을 뜻해요.

오버플로우 검사 (Overflow Checks)

Decimal 계산 중에 정수 오버플로우가 발생할 수 있어요. 소수의 초과 자릿수는 버려져요(반올림되지 않아요). 정수 부분의 초과 자릿수는 예외를 일으켜요. 오버플로우 검사는 Decimal128과 Decimal256에는 구현되지 않아요. 오버플로우가 발생하면 예외가 던져지지 않고 잘못된 결과가 반환돼요.

SELECT toDecimal32(2, 4) AS x, x / 3
┌──────x─┬─divide(toDecimal32(2, 4), 3)─┐
│ 2.0000 │                       0.6666 │
└────────┴──────────────────────────────┘
SELECT toDecimal32(4.2, 8) AS x, x * x
DB::Exception: Scale is out of bounds.
SELECT toDecimal32(4.2, 8) AS x, 6 * x
DB::Exception: Decimal math overflow.

오버플로우 검사는 연산을 느리게 만들어요. 오버플로우가 불가능하다는 것을 안다면 decimal_check_overflow 설정으로 검사를 끄는 게 합리적이에요. 검사를 끄고 오버플로우가 발생하면 결과가 틀려져요.

SET decimal_check_overflow = 0;
SELECT toDecimal32(4.2, 8) AS x, 6 * x
┌──────────x─┬─multiply(6, toDecimal32(4.2, 8))─┐
│ 4.20000000 │                     -17.74967296 │
└────────────┴──────────────────────────────────┘

오버플로우 검사는 산술 연산뿐 아니라 값 비교에서도 일어나요.

SELECT toDecimal32(1, 8) < 100
DB::Exception: Can't compare.

더 알아보기 (Learn more)