IEEE 754 부동소수점
컴퓨터는 0.1이나 3.14 같은 실수를 어떻게 저장할까요? 대부분의 컴퓨터는 IEEE 754 표준의 부동소수점 형식을 씁니다. 수를 과학 표기법처럼 부호, 지수, 가수 세 부분으로 나눠 저장하고, 소수점의 위치를 지수로 옮긴다는 뜻에서 부동(floating)소수점이라 부릅니다. 32비트 단정밀도(float)는 부호 1비트, 지수 8비트, 가수 23비트이고, 64비트 배정밀도(double)는 부호 1비트, 지수 11비트, 가수 52비트입니다. 비트 수가 정해져 있으므로 모든 실수를 정확히 담을 수는 없고, 가장 가까운 값으로 반올림해 저장합니다.
그래서 여러 언어에서 0.1 + 0.2 == 0.3이 거짓이 되는 일이 생깁니다. 계산이 틀린 것이 아니라 10진수 0.1, 0.2, 0.3이 2진수로는 무한히 반복되는 소수라서 각각 가까운 근사값으로 저장되고, 그 근사값끼리 계산하면 0.3의 근사값과 아주 조금 다른 결과가 나오기 때문입니다. 이 토픽에서는 값이 비트로 어떻게 저장되는지, 오차가 어디서 생기는지, 실수를 비교하고 더할 때 무엇을 조심해야 하는지 봅니다.
핵심 개념
부동소수점 표현
수를 (−1)^부호 × 1.가수 × 2^지수 꼴로 저장합니다. 지수가 소수점의 위치를 정하고, 가수가 유효 숫자를 정합니다. 같은 비트 수로 아주 작은 수부터 아주 큰 수까지 표현하는 대신 정밀도에 한계가 생깁니다.
핵심 개념
부호 · 지수 · 가수
float은 1 + 8 + 23비트, double은 1 + 11 + 52비트입니다. 부호 비트는 양수와 음수, 지수 필드는 크기, 가수(소수부) 필드는 유효 숫자를 담습니다. 가수 비트가 많을수록 더 촘촘하게 표현합니다.
핵심 개념
바이어스와 정규화
지수는 음수도 표현해야 하므로 바이어스(float 127, double 1023)를 더해 저장합니다. 정규화된 수(지수 필드가 0과 모두 1이 아닌 경우)는 가수의 맨 앞이 항상 1이어서 그 1을 저장하지 않고 생략하며, 읽을 때 1을 붙여 해석합니다.
핵심 개념
특수한 값
지수 필드가 모두 1이면 가수가 0일 때 ±무한대, 아닐 때 NaN(숫자가 아님)입니다. 지수 필드가 모두 0이면 가수가 0일 때 ±0, 아닐 때 정규화되지 않은 비정규수(subnormal)로 0 근처를 더 촘촘히 표현합니다.
핵심 개념
반올림 오차와 머신 엡실론
표현할 수 없는 값은 가장 가까운 값으로 반올림됩니다. 1과 그다음으로 큰 표현 가능한 수의 간격을 보통 머신 엡실론이라 하며 double은 2^-52, float은 2^-23입니다(교재에 따라 반올림 오차 상한인 그 절반을 부르기도 합니다). 수가 커질수록 이웃한 수 사이의 간격도 커집니다.
핵심 개념
float과 double
double은 가수가 52비트라 float(23비트)보다 촘촘하게 표현하고, 지수가 11비트라 표현 범위도 훨씬 넓습니다. 자바의 float과 double, 자바스크립트의 Number가 이 형식을 쓰고, 파이썬의 float은 보통 C의 double, 즉 거의 모든 플랫폼에서 이 형식으로 구현됩니다.
부동소수점의 배경
컴퓨터마다 부동소수점 형식이 달라 같은 프로그램이 기계마다 다른 결과를 내던 시기가 있었고, 이를 통일하려는 표준이 IEEE 754입니다. 이 표준은 값의 형식뿐 아니라 반올림 규칙, 무한대와 NaN 같은 특수한 값의 처리, 예외 상황까지 정해 두어서 표준을 따르는 하드웨어와 언어는 같은 계산에 같은 결과를 내도록 했습니다. 표준은 여러 번 개정되었습니다.
숫자로 확인하기
자주 하는 오해
면접에서 이렇게 물어요
면접 질문
0.1 + 0.2가 0.3과 다른 이유는?
0.1, 0.2, 0.3이 2진수로 무한히 반복되는 소수라 각각 가장 가까운 근사값으로 저장되고, 그 근사값들을 더한 결과가 0.3의 근사값과 마지막 자리에서 다르기 때문입니다.
면접 질문
float과 double의 비트 구성은?
float은 부호 1, 지수 8, 가수 23비트로 32비트이고 double은 부호 1, 지수 11, 가수 52비트로 64비트입니다. 지수 바이어스는 각각 127과 1023입니다.
면접 질문
실수는 어떻게 비교해야 하나요?
== 대신 두 값의 차이의 절댓값이 허용 오차보다 작은지 확인합니다. 값이 크거나 작을 때는 절대 오차 대신 상대 오차를 쓰고, 언어가 제공하는 비교 함수(예: 파이썬의 math.isclose)가 있으면 활용합니다.
면접 질문
돈 계산에 double을 써도 되나요?
이진 부동소수점은 0.1 같은 십진 소수를 정확히 저장하지 못하므로, 정확한 십진 계산이 필요하면 십진 타입(자바의 BigDecimal, 파이썬의 decimal)이나 최소 단위의 정수로 계산합니다.
실제 시스템에서는
함께 보면 좋은 토픽
참고 자료
float의 비트를 직접 꺼내 보기
float x = 6.5f; int bits = Float.floatToIntBits(x); int sign = bits >>> 31; // 부호 1비트 int exponent = (bits >>> 23) & 0xFF; // 지수 8비트 int fraction = bits & 0x7FFFFF; // 가수 23비트 // 6.5 → sign 0, exponent 129, fraction 5242880 double value = (sign == 0 ? 1 : -1) * (1 + fraction / Math.pow(2, 23)) * Math.pow(2, exponent - 127);
Float.floatToIntBits는 float을 같은 비트 패턴의 int로 바꿔 줍니다. 최상위 비트가 부호, 그다음 8비트가 지수, 하위 23비트가 가수입니다. 정규화된 수는 가수 앞에 생략된 1을 붙여 1 + fraction / 2^23으로 읽고, 지수에서 바이어스 127을 빼서 2의 거듭제곱을 곱합니다. 이 식은 정규화된 수에만 맞고, 지수가 0이거나 255인 특수한 값(0, 비정규수, 무한대, NaN)은 별도로 해석해야 합니다.
실수 비교 — == 대신 허용 오차
double a = 0.1 + 0.2; double b = 0.3; System.out.println(a == b); // false System.out.println(a); // 0.30000000000000004 double eps = 1e-9; System.out.println(Math.abs(a - b) < eps); // true // 값의 크기에 비례하는 상대 오차로 비교 boolean close = Math.abs(a - b) <= eps * Math.max(Math.abs(a), Math.abs(b));
0.1 + 0.2는 0.30000000000000004라서 0.3과 ==로 비교하면 거짓입니다. 두 값의 차이가 허용 오차보다 작은지로 같음을 판단하는 것이 일반적입니다. 허용 오차는 값의 크기에 맞춰 정해야 합니다. 1e-9 같은 고정값은 값이 1 근처일 때는 알맞아도 아주 크거나 아주 작은 수에서는 너무 느슨하거나 너무 엄격할 수 있어, 큰 값에는 상대 오차를 씁니다. 어떤 허용 오차가 알맞은지는 계산의 성격에 달려 있으므로 정해진 정답은 없습니다.
정수가 건너뛰어지는 구간과 십진 계산
float big = 16777216f; // 2^24 System.out.println(big + 1 == big); // true: float은 +1을 표현하지 못함 BigDecimal p = new BigDecimal("0.1"); BigDecimal q = new BigDecimal("0.2"); System.out.println(p.add(q)); // 0.3 (정확한 십진 계산)
float은 2^24부터 이웃한 수의 간격이 2가 되어 16777216f + 1이 다시 16777216이 됩니다. 큰 수에 작은 수를 더하는 계산이 값을 바꾸지 못할 수 있다는 뜻입니다. 정확한 십진 계산이 필요하면 문자열로 만든 BigDecimal을 씁니다. 숫자 리터럴 0.1이 아니라 문자열 "0.1"로 만들어야 하는데, double 리터럴에서 만들면 이미 근사값이 들어가기 때문입니다. BigDecimal은 double보다 느리고 메모리를 더 쓰므로 정확한 십진 값이 필요한 곳에 한정해 씁니다.
Q1
IEEE 754 단정밀도(32비트) 부동소수점의 비트 구성은?
Q2
많은 언어에서 0.1 + 0.2 == 0.3 이 false가 되는 이유는?
Q3
부동소수점 두 수가 같은지 비교할 때 권장되는 방법은?