2026년 03월 08일

🔢 0과 1로 숫자 표현하기

Java Spring Boot
Cover Image

🔢 0과 1로 숫자 표현하기

💡 핵심 질문: 컴퓨터는 왜 0.1 + 0.2 == 0.3false일까요? 이 질문의 답은 컴퓨터가 숫자를 저장하는 방식에 있습니다.


1. 2진법 — 컴퓨터의 언어

컴퓨터는 0과 1만 이해합니다. 그래서 2진법(binary) 을 사용합니다.

2진수 표기법

int decimal = 107;          // 10진수 (기본)
int binary  = 0b1101011;    // 2진수  (0b 접두사)
int hex     = 0x6B;         // 16진수 (0x 접두사)

System.out.println(decimal == binary);  // true (모두 같은 값 107)
System.out.println(decimal == hex);     // true

Java에서 2진수 활용: 비트 연산자(&, |, ^, ~, <<, >>)를 사용할 때 2진수 표현이 직관적입니다. 예를 들어 권한 관리, 플래그 처리, 성능 최적화 등에서 자주 등장합니다.


2. 16진법 — 2진수의 단점을 보완하다

2진수는 치명적인 단점이 있습니다. 숫자가 너무 길어집니다.

10진수  128
2진수   10000000   ← 8자리
16진수  80         ← 2자리  ✅

그래서 컴퓨터 세계에서는 2진수 대신 16진법(hexadecimal) 을 많이 활용합니다.

16진법 체계

16진법은 숫자 15를 넘어가는 시점에 자리올림합니다. 10~15를 표현하기 위해 알파벳 A~F를 사용합니다.

10진수2진수16진수
000000
910019
101010A
111011B
121100C
131101D
141110E
151111F
161000010

2진수 ↔ 16진수 빠른 변환

4비트(1니블) = 16진수 1자리가 정확히 대응됩니다.

2진수:  1101  0111
         ↓      ↓
16진수:   D      7    →  0xD7  (10진수 215)

개발 현장에서 16진수를 쓰는 곳

// 색상 코드 (RGB)
int red   = 0xFF0000;
int green = 0x00FF00;
int blue  = 0x0000FF;

// 비트 마스크
int mask = 0xFF;  // 하위 8비트만 추출

// 해시코드 출력 시
System.out.printf("%08X%n", obj.hashCode());

그 외에도 MAC 주소 (A1:B2:C3:D4:E5:F6), IPv6 주소, 메모리 주소, 색상값 등 개발 전반에서 16진수가 활용됩니다.


3. 부동 소수점 — 소수를 저장하는 방법

왜 "부동(浮動)" 소수점일까?

소수점이 고정되어 있지 않고 유동적(floating)으로 이동하기 때문입니다.

123.456  =  1.23456 × 10²   (소수점을 왼쪽으로 2번 이동)
           =  12345.6 × 10⁻²  (소수점을 오른쪽으로 2번 이동)

2진수에서도 동일한 원리로 표현합니다.

107.6640625 (10진수)
= 1101011.1010101 (2진수)
= 1.1010111010101 × 2⁶   ← 정규화 표현 (가수부 정수 = 1)

IEEE 754 — 표준 저장 방식

오늘날 거의 모든 컴퓨터와 언어가 따르는 소수 저장 표준입니다.

필드역할비고
부호(Sign)0 = 양수, 1 = 음수1비트
지수(Exponent)소수점 이동 횟수바이어스 값 더해서 저장
가수(Fraction)소수 부분 (1.xxx의 xxx)정수부 1은 생략

107.6640625를 32비트로 저장하는 과정

Step 1. 2진수 변환
  107.6640625 → 1101011.1010101(₂)

Step 2. 정규화 (가수부 정수 = 반드시 1)
  1101011.1010101 → 1.1010111010101 × 2⁶

Step 3. 가수 저장 (정수부 1 생략, 23비트 패딩)
  1010111010101 → 10101110101010000000000

Step 4. 지수 저장 (바이어스 127 더하기)
  6 + 127 = 133 → 10000101(₂)

Step 5. 최종 32비트 조합
  [0] [10000101] [10101110101010000000000]
   ↑      ↑               ↑
  부호   지수(133)      가수(소수부분)
최종 비트: 0 10000101 10101110101010000000000
16진수:   42 D7 54 00

실제로 확인해보면?

import struct
print(struct.pack('>f', 107.6640625).hex())
# 출력: 42d75400  ✅

4. 부동 소수점 오차 — 왜 0.1 + 0.2 ≠ 0.3 인가

문제 확인

double a = 0.1;
double b = 0.2;
double c = 0.3;

System.out.println(a + b == c);        // false ❗
System.out.println(a + b);             // 0.30000000000000004

Java뿐 아니라 Python, JavaScript, C/C++ 모두 동일합니다.

왜 이런 오차가 생길까?

핵심은 "10진수 소수가 항상 2진수로 딱 떨어지지 않는다" 는 점입니다.

1/3 → 10진수로 표현 → 0.3333...  (무한 반복, 딱 안 떨어짐)
0.1 → 2진수로 표현 → 0.0001100110011...  (무한 반복, 딱 안 떨어짐!)

메모리는 유한하기 때문에 무한히 반복되는 소수를 어느 지점에서 잘라버립니다. 이 잘린 부분이 바로 부동 소수점 오차의 원인입니다.

실제로 저장된 0.1  →  0.1000000000000000055511151231257827021181583404541015625
실제로 저장된 0.2  →  0.200000000000000011102230246251565404236316680908203125
합계             →  0.3000000000000000444089209850062616169452667236328125
                      ≠ 0.3 !!!

Java에서 오차 없이 소수 다루기

BigDecimal 사용 (가장 정확)

import java.math.BigDecimal;

BigDecimal a = new BigDecimal("0.1");  // 문자열로 생성해야 정확!
BigDecimal b = new BigDecimal("0.2");
BigDecimal c = new BigDecimal("0.3");

System.out.println(a.add(b).equals(c));  // true ✅
System.out.println(a.add(b));            // 0.3 ✅

② 허용 오차 범위 비교 (epsilon 비교)

double a = 0.1 + 0.2;
double b = 0.3;
double epsilon = 1e-9;  // 허용 오차

System.out.println(Math.abs(a - b) < epsilon);  // true ✅

③ 정수로 변환 후 계산

// 금액 계산: 원 단위로 변환 후 계산
long priceInCents = 1099;  // 10.99원을 1099로 저장
long taxInCents   = 88;    // 0.88원을 88로 저장
long totalCents   = priceInCents + taxInCents;  // 1187
double total      = totalCents / 100.0;         // 11.87

⚠️ 실무 주의사항: 금융 계산, 세금, 정밀 측정값 등에서는 반드시 BigDecimal 또는 정수 연산을 사용하세요. double로 금액 계산을 하면 원단위 오차가 발생할 수 있습니다.


5. 핵심 요약

컴퓨터의 숫자 표현

  10진수  ──변환──►  2진수 (0b...)   ← CPU가 실제로 사용
                 ──변환──►  16진수 (0x...)  ← 사람이 읽기 편하게 축약

  소수  ──IEEE 754──►  [부호 1bit | 지수 8or11bit | 가수 23or52bit]
                              ↑
                        무한 소수는 잘라서 저장 → 오차 발생!
진법특징사용 예
2진수CPU가 이해하는 언어비트 연산, 논리 처리
16진수2진수 4자리 = 1자리로 압축색상, 메모리 주소, MAC
부동 소수점IEEE 754, 소수 저장 방식float(32bit), double(64bit)
BigDecimal정밀한 10진 소수 계산금융, 세금 계산

← 목록으로 돌아가기