C++ float와 double의 내부 구조
C++의 float와 double은 일반적으로 IEEE 754 부동소수점 형식을 사용한다.
부동소수점은 실수를 다음 세 부분으로 나누어 저장하는 방식이다.
부호 × 유효숫자 × 2의 지수
10진수의 과학적 표기법과 비슷하다.
12345 = 1.2345 × 10⁴
0.00123 = 1.23 × 10⁻³
컴퓨터에서는 10의 거듭제곱 대신 2의 거듭제곱을 사용한다.
1.10101₂ × 2³
지수에 따라 소수점 위치가 이동하므로 소수점이 고정되어 있지 않다는 의미에서 부동소수점이라고 부른다.
이진수 정규화
10진수 13.25를 이진수로 변환하면 다음과 같다.
13.25₁₀ = 1101.01₂
이를 정규화하면 다음과 같다.
1.10101₂ × 2³
소수점을 이동하여 맨 앞에 1만 남기는 형태이다.
정규화된 이진수는 일반적으로 다음 형태를 가진다.
1.xxxxx₂ × 2ⁿ
여기서 부호, 지수, 유효숫자를 각각 저장한다.
float의 구조
일반적인 float는 32비트로 구성된다.
[부호 1비트] [지수 8비트] [가수부 23비트]
각 영역의 역할은 다음과 같다.
| 비트 수 | 역할 | |
| 부호 | 1 | 양수와 음수를 구분한다. |
| 지수 | 8 | 숫자의 크기와 소수점 위치를 표현한다. |
| 가수부 | 23 | 숫자의 세부적인 유효 정보를 표현한다. |
개념적인 값 계산식은 다음과 같다.
값 = (-1)부호 × 1.가수부 × 2지수
실제 IEEE 754에서는 지수에 바이어스를 적용하고, 특수 값과 서브노멀 값을 별도로 처리한다.
double의 구조
일반적인 double은 64비트로 구성된다.
[부호 1비트] [지수 11비트] [가수부 52비트]
| 전체 비트 | 부호 | 지수 | 가수부 | |
| float | 32 | 1 | 8 | 23 |
| double | 64 | 1 | 11 | 52 |
double은 float보다 지수와 가수부에 더 많은 비트를 사용한다. 따라서 더 넓은 범위와 높은 정밀도를 제공한다.
Hidden Bit
정규화된 이진수는 맨 앞자리가 항상 1이다.
1.10101₂
1.00011₂
1.11100₂
맨 앞의 1은 항상 존재하므로 실제 비트에 저장하지 않아도 복원할 수 있다.
실제 유효숫자: 1.10101
저장되는 부분: 10101
생략되는 부분: 1.
생략된 선두 1을 다음과 같이 부른다.
- Hidden Bit
- Implicit Leading One
- 암묵적 선두 1
따라서 float의 가수부 필드는 23비트이지만 실제 유효 정밀도는 24비트이다.
저장 가수부 23비트
+ 암묵적 선두 1비트
= 유효 정밀도 24비트
double 역시 저장 가수부는 52비트이지만 실제 유효 정밀도는 53비트이다.
이진 정밀도와 십진 유효 자릿수
float의 실제 유효 정밀도는 24비트이다.
2²⁴ = 16,777,216
이를 십진 자릿수로 환산하면 다음과 같다.
log₁₀(2²⁴)
≈ 7.22
따라서 float는 약 6~7자리 정도의 십진 유효숫자 정보에 해당하는 정밀도를 가진다.
double은 53비트 정밀도를 가진다.
log₁₀(2⁵³)
≈ 15.95
일반적으로 다음과 같이 설명한다.
| 실제 이진 정밀도 | 십진 유효 자릿수 | |
| float | 24비트 | 약 6~7자리 |
| double | 53비트 | 약 15~16자리 |
유효 자릿수의 의미
유효 자릿수는 소수점 이하의 자릿수가 아니다. 숫자 전체에서 의미 있게 유지되는 숫자의 개수를 뜻한다.
1234567
123.4567
0.001234567
위 값들은 모두 약 7개의 유효 숫자를 가진다.
따라서 값의 크기가 커지면 작은 변화량을 표현하기 어려워진다.
float value = 100000000.0f;
value += 1.0f;
100000000 부근에서는 인접한 float 값 사이의 간격이 1보다 크다. 따라서 1.0f를 더해도 저장된 값이 변하지 않을 수 있다.
digits10과 max_digits10
C++에서는 자료형의 정밀도 정보를 std::numeric_limits로 확인할 수 있다.
#include <iostream>
#include <limits>
int main()
{
std::cout
<< std::numeric_limits<float>::digits10
<< '\n';
std::cout
<< std::numeric_limits<float>::max_digits10
<< '\n';
}
일반적인 float에서는 다음 값을 가진다.
digits10 = 6
max_digits10 = 9
digits10은 10진수 값을 부동소수점으로 변환한 뒤 다시 10진수로 변환했을 때 안정적으로 유지되는 최소 자릿수를 의미한다.
max_digits10은 부동소수점 값을 문자열로 출력했다가 다시 읽을 때 원래 값을 복원하기 위해 필요한 최대 자릿수를 의미한다.
두 값은 서로 다른 목적을 가진다.
핵심 정리
- 부동소수점은 부호, 지수, 가수부로 실수를 표현한다.
- float는 32비트, double은 64비트를 사용한다.
- float의 가수부 필드는 23비트이지만 Hidden Bit을 포함하면 실제 정밀도는 24비트이다.
- double의 실제 유효 정밀도는 53비트이다.
- float는 약 6~7자리, double은 약 15~16자리의 십진 유효 자릿수를 제공한다.
- 유효 자릿수는 소수점 이하가 아니라 숫자 전체의 정밀도를 의미한다.
'C++ > C++ 문법과 핵심 개념' 카테고리의 다른 글
| C++ 동적 메모리 할당은 왜 필요할까? - Stack과 Heap (0) | 2026.08.27 |
|---|---|
| C++ 멤버 접근 제어 방법 - Getter/Setter, friend, using (1) | 2026.08.26 |
| C++ Struct와 Class의 차이, 서로 상속할 수 있을까? (0) | 2026.08.26 |
| [C++ 실수 자료형 3] 부동소수점 오차와 대응 방법 (0) | 2026.08.06 |
| [C++ 실수 자료형 1] 이진수로 소수를 표현하는 방법 (0) | 2026.08.06 |