부동소수점 타입(FLOAT 및 DOUBLE)
설명
Doris는 FLOAT와 DOUBLE이라는 두 가지 부동소수점 데이터 타입을 제공합니다. 이들은 IEEE 754 부동소수점 연산 표준을 따르는 가변 정밀도 숫자 타입입니다.
| 타입 | 별칭 | 저장 크기 | 설명 |
|---|---|---|---|
| FLOAT | FLOAT4, REAL | 4바이트 | 단정밀도 부동소수점 |
| DOUBLE | FLOAT8, DOUBLE PRECISION | 8바이트 | 배정밀도 부동소수점 |
값 범위
FLOAT
Doris는 IEEE-754 단정밀도 부동소수점 수를 사용하며, 값 범위는 다음과 같습니다.
- -∞ (-Infinity)
- [-3.402E+38, -1.175E-37]
- 0
- [1.175E-37, 3.402E+38]
- +∞ (+Infinity)
- NaN (숫자가 아님)
자세한 내용은 C++ float 타입과 Wikipedia의 단정밀도 부동소수점 형식을 참조하세요.
DOUBLE
Doris는 IEEE-754 배정밀도 부동소수점 수를 사용하며, 값 범위는 다음과 같습니다.
- -∞ (-Infinity)
- [-1.79769E+308, -2.225E-307]
- 0
- [+2.225E-307, +1.79769E+308]
- +∞ (+Infinity)
- NaN (숫자가 아님)
자세한 내용은 C++ double 타입과 Wikipedia의 배정밀도 부동소수점 형식을 참조하세요.
특수 값
부동소수점 타입에는 일반적인 숫자 값 외에도 IEEE 754 표준을 따르는 몇 가지 특수 값이 있습니다.
Infinity또는Inf: 양의 무한대-Infinity또는-Inf: 음의 무한대NaN: 숫자가 아님
이러한 특수 값은 CAST 변환으로 생성할 수 있습니다.
mysql> select cast('NaN' as double), cast('inf' as double), cast('-Infinity' as double);
+-----------------------+-----------------------+-----------------------------+
| cast('NaN' as double) | cast('inf' as double) | cast('-Infinity' as double) |
+-----------------------+-----------------------+-----------------------------+
| NaN | Infinity | -Infinity |
+-----------------------+-----------------------+-----------------------------+
부동소수점 수에는 직관적이지 않은 특성도 있습니다. 영을 나타내는 서로 다른 두 값인 +0과 -0이 존재합니다.
대부분의 상황에서는 같은 값으로 간주하지만, 부호 비트는 서로 다릅니다.
mysql> select cast('+0.0' as double), cast('-0.0' as double);
+------------------------+------------------------+
| cast('+0.0' as double) | cast('-0.0' as double) |
+------------------------+------------------------+
| 0 | -0 |
+------------------------+------------------------+
부동소수점 연산
산술 연산
Doris의 부동소수점 수는 덧셈, 뺄셈, 곱셈, 나눗셈과 같은 일반적인 산술 연산을 지원합니다.
Doris는 부동소수점 수를 0으로 나누는 경우 IEEE 754 표준을 완전히 따르지 않는다는 점에 유의해야 합니다.
이 경우 Doris는 PostgreSQL의 구현 방식을 따르며, 0으로 나눌 때 특수 값을 생성하는 대신 SQL NULL을 반환합니다.
| 표현식 | PostgreSQL | IEEE 754 | Doris |
|---|---|---|---|
| 1.0 / 0.0 | Error | Infinity | NULL |
| 0.0 / 0.0 | Error | NaN | NULL |
| -1.0 / 0.0 | Error | -Infinity | NULL |
| 'Infinity' / 'Infinity' | NaN | NaN | NaN |
| 1.0 / 'Infinity' | 0.0 | 0.0 | 0 |
| 'Infinity' - 'Infinity' | NaN | NaN | NaN |
| 'Infinity' - 1.0 | Infinity | Infinity | Infinity |
비교 연산
IEEE 표준에서 정의하는 부동소수점 비교는 일반적인 정수 비교와 몇 가지 중요한 차이가 있습니다. 예를 들어 음의 영과 양의 영은 같은 값으로 간주하며, NaN은 자기 자신을 포함한 어떤 값과도 같지 않습니다. 모든 유한한 부동소수점 수는 +∞보다 작고 -∞보다 큽니다.
Doris는 결과의 일관성과 예측 가능성을 보장하기 위해 NaN을 IEEE 표준과 다르게 처리합니다. Doris에서는 NaN을 Infinity를 포함한 다른 모든 값보다 큰 값으로 간주하며, NaN은 NaN과 같습니다.
mysql> select * from sort_float order by d;
+------+-----------+
| id | d |
+------+-----------+
| 5 | -Infinity |
| 2 | -123 |
| 1 | 123 |
| 4 | Infinity |
| 8 | NaN |
| 9 | NaN |
+------+-----------+
mysql> select
cast('Nan' as double) = cast('Nan' as double) ,
cast('Nan' as double) > cast('Inf' as double) ,
cast('Nan' as double) > cast('123456.789' as double);
+-----------------------------------------------+-----------------------------------------------+------------------------------------------------------+
| cast('Nan' as double) = cast('Nan' as double) | cast('Nan' as double) > cast('Inf' as double) | cast('Nan' as double) > cast('123456.789' as double) |
+-----------------------------------------------+-----------------------------------------------+------------------------------------------------------+
| 1 | 1 | 1 |
+-----------------------------------------------+-----------------------------------------------+------------------------------------------------------+
부동소수점 정밀도 문제
근삿값과 정밀도 손실
부동소수점 수는 본질적으로 값을 근사하여 표현합니다. 따라서 일부 십진수 값은 부동소수점 수의 이진 표현으로 정확하게 저장할 수 없으며, 대신 근삿값으로 저장됩니다. 이로 인해 저장하고 읽어 오는 과정에서 미세한 차이가 발생할 수 있습니다.
예를 들면 다음과 같습니다.
mysql> SELECT CAST(1.3 AS FLOAT) - CAST(0.7 AS FLOAT) = CAST(0.6 AS FLOAT);
+--------------------------------------------------------------+
| CAST(1.3 AS FLOAT) - CAST(0.7 AS FLOAT) = CAST(0.6 AS FLOAT) |
+--------------------------------------------------------------+
| 0 |
+--------------------------------------------------------------+
부동소수점 표현 오차로 인해 이 표현식의 평가 결과는 예상과 달리 TRUE가 아닐 수 있습니다.
결합법칙을 따르지 않는 연산
부동소수점 연산은 정밀도에 한계가 있으므로 이론적인 수학 연산과 다른 계산 특성을 보입니다. 부동소수점 덧셈과 곱셈은 결합법칙과 분배법칙을 엄밀하게 따르지 않습니다.
따라서 계산 순서가 다르면 결과도 조금씩 달라질 수 있습니다. Doris는 MPP 아키텍처를 사용하므로 정확한 데이터 처리 순서를 보장할 수 없습니다. 이 때문에 입력 데이터가 같더라도 부동소수점 수를 사용하는 계산은 실행할 때마다 결과가 조금씩 달라질 수 있습니다.
집계 함수
부동소수점 값에 집계 함수를 적용하면 오차가 누적될 수 있으며, 특히 대규모 데이터셋을 처리할 때 이러한 문제가 발생하기 쉽습니다. 데이터에 매우 크거나 작은 값이 포함되어 있으면 오차가 더욱 커질 수 있습니다. 계산 순서가 정해져 있지 않으므로, 데이터에 극단적인 값이 포함된 경우 같은 집계 함수를 여러 번 실행해도 결과가 달라질 수 있습니다.
JOIN 연산
집계 함수와 마찬가지로 부동소수점 컬럼을 기준으로 테이블 JOIN을 수행하는 것은 권장하지 않습니다. 부동소수점 수의 정밀도 문제로 인해 이론적으로 같은 두 값도 내부 표현이 조금씩 다를 수 있으며, 이 때문에 일치하는 값으로 처리되지 않을 수 있습니다.
부동소수점 출력
4.1.4부터 Doris는 부동소수점 수를 문자열로 변환할 때 값을 손실 없이 왕복 변환할 수 있는 가장 짧은 문자열을 출력합니다. 이 문자열을 다시 FLOAT / DOUBLE로 파싱하면 원래 값과 정확히 같은 이진 값이 생성되며, 불필요한 자릿수는 출력하지 않습니다.
출력 형식 규칙은 다음과 같습니다.
- 십진 지수가
[-4, 16)범위에 있으면 고정소수점 표기법을 사용합니다. - 그 외에는 과학적 표기법을 사용합니다. 소문자
e와 명시적인 부호를 사용하고 지수를 최소 두 자리로 표시합니다. 예:1.23456e-05. - 양의 영과 음의 영은 구분해서 출력합니다(
0및-0). NaN,Infinity,-Infinity는 기존 표기를 유지합니다.
mysql> select cast('0.000123456' as double), cast('0.0000123456' as double);
+-------------------------------+--------------------------------+
| cast('0.000123456' as double) | cast('0.0000123456' as double) |
+-------------------------------+--------------------------------+
| 0.000123456 | 1.23456e-05 |
+-------------------------------+--------------------------------+
4.1.4 이전 버전에서는 고정된 수의 유효 자릿수(FLOAT는 약 7자리, DOUBLE은 약 16자리)를 출력했으므로, 출력된 문자열을 다시 파싱해도 항상 원래 값으로 복원되지는 않았습니다. 가장 짧은 왕복 변환 표현으로 전환한 이후에도 저장된 값은 바뀌지 않으며, 텍스트 표현만 달라질 수 있습니다. 예를 들면 다음과 같습니다.
| 4.1.4 이전 | 4.1.4 이상 |
|---|---|
246.9120025634766 | 246.91200256347656 |
15241.38425247656 | 15241.384252476564 |
11.11107561317707 | 11.111075613177073 |
일부 텍스트 출력에서 DOUBLE 값 2.0을 2.0으로 출력 | 2로 출력 |
0.0000123456 | 1.23456e-05 |
이 변경은 다양한 출력 경로에 영향을 줍니다. 여기에는 MySQL 프로토콜 쿼리 결과, CAST(... AS STRING), ARRAY / MAP / STRUCT 요소 표시, JSON / VARIANT 표시, CSV / Parquet / ORC 형식의 SELECT INTO OUTFILE 텍스트 출력, EXPORT, Stream Load 반환값, 외부 테이블(Hive / Iceberg / Paimon / JDBC / ES / TVF)의 부동소수점 컬럼이 포함됩니다.
다운스트림 시스템에서 부동소수점 수의 문자열 표현이 정확히 일치하는지 비교하는 경우(예: 내보낸 파일을 비교할 때), 업그레이드 후 이 변경에 맞게 조정하세요.
권장 사항
-
적절한 데이터 타입 선택: 재무 계산이나 정확한 숫자 값이 필요한 경우에는 부동소수점 타입 대신
DECIMAL타입을 사용하세요. -
동등 비교 시 주의: 두 부동소수점 값이 같은지 직접 비교하는 것은 피하세요. 특히 JOIN 연산에서 주의해야 합니다.
-
문자열 변환 시 주의: 부동소수점 수를 문자열로 변환한 뒤 다시 원래 타입으로 변환하면 정밀도가 추가로 손실될 수 있습니다.
-
플랫폼 간 차이 이해: 데이터베이스 시스템마다 부동소수점 연산을 처리하는 방식에 미묘한 차이가 있을 수 있습니다. 특히 NaN과 Infinity 같은 특수한 경우를 처리할 때 차이가 나타날 수 있습니다(대부분의 데이터베이스 시스템은 대체로 IEEE 표준을 따릅니다).
-
표시에 맞게 결과 반올림: 부동소수점 계산 결과를 표시할 때는 사용자가 겪는 정밀도 문제를 줄일 수 있도록 적절한 반올림을 고려하세요.
키워드
FLOAT, FLOAT4, REAL, DOUBLE, DOUBLE PRECISION, FLOAT8, 부동소수점