FLOAT 비교 실패 할때

TNT·2026년 1월 11일

mssql

목록 보기
5/9

코딩을 하거나 db에서 프로시저로 다들 개발을 하는데
바로 비교 관련해서 값이 같은지 체크 하는 if 관련으로 실패하는경우가 있다.
바로 float 형인데 보통 개발 하다보면 0.0003 퍼센트 확률로 나오는 아이템을 만들고 싶다거나 등등 이런 관련해서 작업을 합니다.

문제: 근사값 데이터 타입

SQL Server의 숫자 데이터 타입은 크게 두 가지로 나뉩니다.
Exact Numeric: DECIMAL, NUMERIC, INT 등. 이들은 값을 10진수 기준으로 정확하게 저장합니다.

Approximate Numeric: FLOAT, REAL 2진수 기반의 부동소수점 방식으로 저장하며, 이 과정에서 대부분의 10진수 소수를 완벽하게 표현하지 못하고 가장 가까운 근사치로 저장합니다.

예를 들어, 10진수 0.1은 2진수로 표현하면 0.0001100110011... 처럼 무한히 반복되는 소수가 됩니다. FLOAT 타입은 제한된 비트 수 안에 이 값을 저장해야 하므로, 어쩔 수 없이 특정 지점에서 값을 잘라내고, 이로 인해 미세한 오차가 발생합니다.

DECLARE @CurrentUserPityRate FLOAT = 99.9; -- 유저의 값 
DECLARE @FinalBoost FLOAT = 0.1; -- 시스템으로 하는 가중치 

SET @CurrentUserPityRate = @CurrentUserPityRate + @FinalBoost;

PRINT '--- FLOAT 타입 직접 비교 ---';
IF @CurrentUserPityRate = 100.0 -- 이게 나올수있는 획득 목록에서 최대치 값 이라고하면 
BEGIN
    PRINT '결과: 100.0과 일치함. 확정 보상을 지급합니다.';
END
ELSE
BEGIN
    PRINT '결과: 100.0과 일치하지 않음.';
END
GO

실행 결과

--- FLOAT 타입 직접 비교 ---
결과: 100.0과 일치하지 않음.

99.9 + 0.1의 결과가 100.0과 일치하지 않는다

좀더 자세하게 비교를 하기위해서 안에 어떻게 저장 되는지 보면

DECLARE @CurrentUserPityRate FLOAT = 99.9;
DECLARE @FinalBoost FLOAT = 0.1;

SET @CurrentUserPityRate = @CurrentUserPityRate + @FinalBoost;

-- STR 함수를 사용하여 float 값의 내부 표현을 최대한 자세히 출력
PRINT '`99.9 + 0.1`의 실제 연산 결과: ' + STR(@CurrentUserPityRate, 25, 20);
PRINT '`100.0`을 FLOAT로 표현한 값:  ' + STR(CAST(100.0 AS FLOAT), 25, 20);
GO

실행 결과

`99.9 + 0.1`의 실제 연산 결과: 100.00000000000001000000
`100.0`FLOAT로 표현한 값:  100.00000000000000000000

결과에서 볼 수 있듯, 99.9와 0.1이 각각 근사치로 저장된 상태에서 연산이 수행되자 그 오차가 누적되어 100.0을 아주 미세하게 초과하는 값이 되었습니다.
이 작은 차이로 인해 = 연산자는 FALSE를 반환된다.

해결 방안

방법1 허용 오차 범위를 사용한 비교

DECLARE @CurrentUserPityRate FLOAT = 99.9 + 0.1;
DECLARE @TargetRate FLOAT = 100.0;
DECLARE @Epsilon FLOAT = 0.000001; -- 허용 오차

PRINT '--- 오차 범위 비교 ---';
IF ABS(@CurrentUserPityRate - @TargetRate) < @Epsilon
BEGIN
    PRINT '결과: 두 값의 차이가 허용 오차 내에 있으므로 일치하는 것으로 간주합니다.';
END
ELSE
BEGIN
    PRINT '결과: 두 값의 차이가 허용 오차를 벗어납니다.';
END
GO

--- 오차 범위 비교 ---
결과: 두 값의 차이가 허용 오차 내에 있으므로 일치하는 것으로 간주합니다.

비교를 할때 값을뺴고 그값이 0.000001 쯤보다 작으면 그냥 같은것으로 취급을 하는것이다.

방법2 정확한 숫자 타입

하지만 이런건 너무 보기가 힘들고 계산이 어렵다...
그래서 하는것이 INT DECIMAL 또는 일정 수수점 이하 잘라버려서 사용을 하는것이 좋지만 개인적인 추천은 그냥 int형을 사용하는걸 제일 추천한다.

DECIMAL을 이용해서 처리 하는과정

DECLARE @CurrentUserPityRate_Dec DECIMAL(18, 10) = 99.9;
DECLARE @FinalBoost_Dec DECIMAL(18, 10) = 0.1;

SET @CurrentUserPityRate_Dec = @CurrentUserPityRate_Dec + @FinalBoost_Dec;

PRINT '--- DECIMAL 타입 직접 비교 ---';
IF @CurrentUserPityRate_Dec = 100.0
BEGIN
    PRINT '결과: 100.0과 정확히 일치함.';
END
ELSE
BEGIN
    PRINT '결과: 100.0과 일치하지 않음.';
END
GO

--- DECIMAL 타입 직접 비교 ---
결과: 100.0과 정확히 일치함.

DECIMAL, NUMERIC 자세한 설명

이걸 보면 좀더 이해 할수있다 float를 10진수로 저장하는것인데 자세한 원리는 문서를 보도록 하자

FLOAT 타입은 과학 및 공학 계산과 같이 매우 넓은 범위의 수를 표현해야 하고 약간의 오차를 감수할 수 있는 경우에 유용합니다.
하지만 확률, 금융 정보, 재고 수량 등 정확한 값이 반드시 보장되어야 하는 데이터에는 부적합합니다.

profile
개발

0개의 댓글