Float

Float (부동소수점) 데이터 타입

정확한 계산이 필요하다면, 특히 높은 정밀도를 요구하는 금융·비즈니스 데이터를 다룬다면 Decimal을 사용하는 걸 고려해 볼게요. ClickHouse의 부동소수점 타입은 Float32(C의 float)와 Float64(C의 double)예요.

출처: 문서

본문

정확한 계산이 필요하다면, 특히 높은 정밀도가 필요한 금융·비즈니스 데이터를 다룬다면 Decimal을 사용하는 걸 고려해야 해요. 부동소수점 숫자(Floating Point Numbers)는 아래처럼 부정확한 결과를 낼 수 있어요.

CREATE TABLE IF NOT EXISTS float_vs_decimal
(
my_float Float64,
my_decimal Decimal64(3)
)
ENGINE=MergeTree
ORDER BY tuple();

# Generate 1 000 000 random numbers with 2 decimal places and store them as a float and as a decimal
INSERT INTO float_vs_decimal SELECT round(randCanonical(), 3) AS res, res FROM system.numbers LIMIT 1000000;
SELECT sum(my_float), sum(my_decimal) FROM float_vs_decimal;

┌──────sum(my_float)─┬─sum(my_decimal)─┐
│ 499693.60500000004 │      499693.605 │
└────────────────────┴─────────────────┘

SELECT sumKahan(my_float), sumKahan(my_decimal) FROM float_vs_decimal;

┌─sumKahan(my_float)─┬─sumKahan(my_decimal)─┐
│         499693.605 │           499693.605 │
└────────────────────┴──────────────────────┘

ClickHouse와 C에서의 동등 타입은 아래와 같아요.

  • Float32 — float.
  • Float64 — double.

ClickHouse의 Float 타입에는 다음 별칭이 있어요.

  • Float32 — FLOAT, REAL, SINGLE.
  • Float64 — DOUBLE, DOUBLE PRECISION.

테이블을 만들 때 부동소수점 숫자의 숫자 매개변수를 설정할 수 있지만(예: FLOAT(12), FLOAT(15, 22), DOUBLE(12), DOUBLE(4, 18)), ClickHouse는 이를 무시해요.

부동소수점 숫자 사용하기 (Using floating-point numbers)

  • 부동소수점 숫자와의 계산은 반올림 오류를 만들 수 있어요.
SELECT 1 - 0.9

┌───────minus(1, 0.9)─┐
│ 0.09999999999999998 │
└─────────────────────┘
  • 계산의 결과는 계산 방법(프로세서 유형과 컴퓨터 시스템의 아키텍처)에 따라 달라져요.
  • 부동소수점 계산은 무한대(Inf)와 "not-a-number"(NaN) 같은 숫자를 만들 수 있어요. 계산 결과를 처리할 때 이를 고려해야 해요.
  • 텍스트에서 부동소수점 숫자를 파싱할 때 결과가 가장 가까운 기계 표현 가능한 숫자가 아닐 수 있어요.

NaN과 Inf

표준 SQL과 달리 ClickHouse는 다음 범주의 부동소수점 숫자를 지원해요.

  • Inf — 무한대(Infinity).
SELECT 0.5 / 0

┌─divide(0.5, 0)─┐
│            inf │
└────────────────┘
  • -Inf — 음의 무한대(Negative infinity).
SELECT -0.5 / 0

┌─divide(-0.5, 0)─┐
│            -inf │
└─────────────────┘
  • NaN — 숫자 아님(Not a number).
SELECT 0 / 0

┌─divide(0, 0)─┐
│          nan │
└──────────────┘

NaN 정렬 규칙은 ORDER BY 절 섹션에서 확인할 수 있어요.

집합 의미론에서의 NaN 값 (NaN values in set semantics)

IEEE 754 표준은 스칼라 비교 NaN = NaN이 false를 반환하도록 NaN을 정의해요. ClickHouse는 = 연산자에 대해 그 규칙을 따르지만, NaN은 단일 값이 아니에요. 지수가 모두 1이고 가수가 0이 아닌 어떤 비트 패턴이든 NaN이에요. 서로 다른 연산과 CPU 아키텍처는 부호 비트나 가수 페이로드가 다른 NaN 값을 만들 수 있어요. 예를 들어:

  • 0./0.은 대부분의 x86 플랫폼에서 부호 비트가 1인 NaN을 만들어요.
  • 리터럴 nan은 부호 비트가 0인 NaN을 만들어요.
  • PR #98230 이후, log의 AArch64 NEON 경로는 음수 입력에서 glibc의 스칼라 log와 부호 비트가 다른 NaN을 반환해요.

ClickHouse의 해시 테이블은 키를 바이트 단위로 비교하므로, 서로 다른 NaN 비트 패턴은 서로 다른 버킷으로 해시되고 DISTINCT, GROUP BY, uniqExact, countDistinct, Float 키의 equi-JOIN을 포함한 집합 의미론 연산에서 서로 다른 값으로 취급돼요.

SELECT countDistinct(arrayJoin([0./0., nan, log(-1.)]));
-- May return 2 or 3 depending on architecture and build, even though all three inputs are NaN.

이것은 IEEE 754와 일관되지만(모든 NaN은 자기 자신을 포함한 모든 다른 값과 같지 않음) 놀라울 수 있어요. 집합 의미론 연산이 모든 NaN 값을 동일하게 취급하길 원한다면 쿼리에서 정규화할 수 있어요.

-- Replace every NaN with a single canonical NaN value
SELECT countDistinct(if(isNaN(x), CAST('nan' AS Float64), x))
FROM (SELECT arrayJoin([0./0., nan, log(-1.)]) AS x);
-- Returns 1.

-- Or exclude NaN values from the set entirely
SELECT countDistinct(if(isNaN(x), NULL, x))
FROM (SELECT arrayJoin([0./0., nan, log(-1.)]) AS x);
-- Returns 0.

같은 접근 방식이 DISTINCT, GROUP BY, JOIN 키에도 적용돼요.

BFloat16

BFloat16은 8비트 지수와 부호, 7비트 가수를 가진 16비트 부동소수점 데이터 타입이에요. 머신러닝과 AI 애플리케이션에 유용해요. ClickHouse는 toFloat32() 또는 toBFloat16 함수로 할 수 있는 Float32와 BFloat16 사이의 변환을 지원해요. 그 외 대부분의 연산은 지원되지 않아요.

더 알아보기 (Learn more)