컴퓨터에서 소수(小數)를 다루는 가장 직관적인 방법은
소수점 위치를 고정하고 정수처럼 계산하는 것이다.
// 고정 소수점 예시
12345678.90
고정 소수점은 정수 연산 기반이기 때문에
계산 결과가 항상 정확하다.
그래서 금융, 회계, 정산처럼
오차가 허용되지 않는 영역에서는 지금도 사용된다.
고정 소수점 예시 (소수점 2자리 기준)
| 실제 값 | 내부 저장 | 결과 |
|---|---|---|
| 0.01 | 1 | 정상 |
| 12345678.90 | 1234567890 | 정상 |
| 0.0000001 | 0 | 손실 |
| 10¹⁰ | 10¹² | 오버플로우 |
고정 소수점 방식은 소수점 위치를 미리 고정해 두고
모든 실수를 동일한 형식으로 저장한다.
예를 들어 소수점 두 자리를 고정하면
0.01이나 12345678.90과 같은 값은 문제없이 표현할 수 있다.
하지만, 0.0000001처럼 매우 작은 수는
소수점 자릿수가 부족해 0으로 잘려 저장되고
10¹⁰과 같은 큰 수는 내부 정수 범위를 초과해
오버플로우가 발생할 수 있다.
이처럼 고정 소수점 방식은
작은 수와 큰 수를 동시에 안정적으로 표현하기 어렵다.
또한 전체 비트 수가 고정되어 있기 때문에
소수부 비트를 늘려 정밀도를 높이면
표현 가능한 범위가 줄어들고, 범위를 넓히면 정밀도가 희생된다.
정밀도: 소수 자리의 정밀도가 아닌 실수 자체의 정확한 표현 자리수를 의미한다.
이러한 정밀도와 범위의 트레이드오프 문제로 인해
보다 유연한 수 표현 방식인 부동 소수점이 등장하게 되었다.
(소수점을 고정하지 않고 숫자를 지수 형태로 표현하는 방식)
부동 소수점(Floating Point)은
소수점 위치를 고정하지 않고 숫자를 지수 형태로 표현하는 방식이다.
// 고정 소수점
[정수부].[소수부] ← 소수점 위치 고정
// 부동 소수점
가수 × 10ⁿ (개념적으로)
가수 × 2ⁿ (컴퓨터 내부)
즉, 소수점이 지수에 따라 움직인다.
자바는 IEEE 754 표준을 따른다.
| 타입 | 크기 | 유효 자릿수(대략) | 표현 범위(대략) |
|---|---|---|---|
| float | 32bit | 약 7자리 | 약 ±10³⁸ |
| double | 64bit | 약 15~16자리 | 약 ±10³⁰⁸ |
유효 자릿수란
오차 없이 신뢰할 수 있는 10진수 자리 수를 의미한다.
즉, Java에서 소수 리터럴이 기본적으로 double인 가장 큰 이유는
float보다 유효 자릿수가 훨씬 많아 정밀도가 높기 때문이다.
반대로, 부동 소수점은 넓은 표현 범위를 제공하지만
2진수 기반 표현으로 인해 정확한 소수 계산이 불가능한 경우가 존재한다.
대표적인 예로 다음과 같은 계산이 있다.
double a = 0.1;
double b = 0.2;
System.out.println(a + b); // 0.30000000000000004
이는 오류나 버그가 아니라,
부동 소수점의 설계 특성상 발생하는 근사 오차이다.
이러한 문제로 인해
금융, 회계, 정산과 같이 정확성이 절대적으로 중요한 영역에서는
부동 소수점 대신 BigDecimal 을 사용한다.
BigDecimal은 자바에서 제공하는
정확한 10진수 계산을 위한 클래스이다.
값 = 정수(unscaledValue) × 10^(-scale)
개념적으로 BigDecimal은
부동 소수점이 아닌 고정 소수점 방식을 사용한다.
new BigDecimal("123.45")
unscaledValue: 12345
scale: 2
즉, 실제로는
정수 값과 소수점 위치(scale)를 분리하여 저장한다.
이 방식은 앞서 설명한 고정 소수점과 동일한 개념이며
정수 기반 연산이기 때문에 소수 오차가 발생하지 않는다.
BigDecimal은 반드시 문자열 기반 생성자를 사용해야 한다.
new BigDecimal(0.1); // ❌ 이미 오차가 포함된 값
new BigDecimal("0.1"); // ✅ 정확한 값
double을 거쳐 생성하면
이미 근사 오차가 포함된 값이 전달되므로
BigDecimal의 장점을 잃게 된다.
| 구분 | 부동 소수점 (float/double) | BigDecimal |
|---|---|---|
| 표현 방식 | 가수 × 2ⁿ | 정수 × 10⁻ˢᶜᵃˡᵉ |
| 기반 | 2진수 | 10진수 |
| 오차 | 발생 가능 | 없음 |
| 표현 범위 | 매우 넓음 | 상대적으로 제한적 |
| 연산 속도 | 빠름 | 느림 |
| 사용 목적 | 과학 계산, 근사값 | 금융, 정산, 정확한 계산 |