한양과학기술고등학교

에듀테크 프로젝트 대시보드

부동소수점 (IEEE 754)

컴퓨터는 왜 0.1 + 0.2 를 0.3 이라고 하지 않을까?

거의 모든 언어에서 0.1 + 0.2 는 0.30000000000000004 가 나온다. 버그가 아니라, 실수(소수)를 64비트에 욱여넣는 방식(IEEE 754 double) 때문이다. 2진수로는 0.1 을 딱 떨어지게 쓸 수 없어서, 컴퓨터는 가장 가까운 값으로 반올림해 저장한다. 아래에서 숫자 하나를 넣고 그 64비트가 어떻게 조립되는지 직접 보자.

부호 (Sign) · 1비트 지수 (Exponent) · 11비트 가수 (Mantissa / Fraction) · 52비트

부호 비트

0
양수 → +1

지수 (저장값 − 1023)

0

가수 (1.____ 형태)

1.0

2진 무한소수

0.1 을 2진수로 쓰면 0.0001100110011… 처럼 무한 반복된다. 10진수의 1/3 = 0.333… 과 같은 상황. 52비트에서 잘리며 오차가 생긴다.

값이 클수록 성긴 간격

가수 비트 수는 고정이라, 수가 커지면 표현 가능한 값 사이 간격(ULP)도 커진다. 2^53 을 넘으면 정수조차 1칸씩 건너뛴다 — 2^53 + 1 은 저장이 안 된다.

특수한 값

지수가 전부 1이면 Infinity 또는 NaN, 전부 0이면 0 또는 아주 작은 비정규 수. 그래서 1e309 는 Infinity 가 된다.

그럼 실무에서는? 돈 계산처럼 오차가 치명적인 곳은 float 대신 정수(센트 단위) 나 Decimal 타입(파이썬 decimal, 자바 BigDecimal)을 쓴다. 비교할 때는 a == b 대신 abs(a - b) < 1e-9 처럼 허용 오차(epsilon) 를 둔다. 화면에 보여줄 때만 round(x, 2) 로 정리한다.

💡 float(32비트)와 double(64비트)의 차이

float 는 지수 8비트 · 가수 23비트로 약 7자리 정밀도, double 은 지수 11비트 · 가수 52비트로 약 15~16자리 정밀도다. 자바스크립트의 number, 파이썬의 float 는 모두 double이다. C/C++ 에서 float 를 쓰면 메모리는 절반이지만 오차는 훨씬 빨리 쌓인다.

💡 0.5, 0.25, 2.75 는 왜 오차가 없을까?

분모가 2의 거듭제곱인 분수(1/2, 1/4, 11/4)는 2진수로 딱 떨어진다. 2.75 = 10.11₂. 반대로 0.1 = 1/10, 0.3 = 3/10 처럼 분모에 5가 들어가면 2진수로 무한소수가 되어 반올림 오차가 생긴다.

💡 그래서 0.1 + 0.2 == 0.3 은 왜 false 인가

저장된 0.1 은 살짝 크고, 0.2 도 살짝 크다. 둘을 더한 결과를 다시 반올림하면 0.30000000000000004 가 되는데, 이건 따로 저장한 0.3(이쪽은 살짝 작게 반올림됨)과 비트가 다르다. 두 근사값이 우연히 같지 않으면 == 는 false다.