정수와 실수 표현
정수와 실수 표현¶
개요¶
컴퓨터에서 수를 표현하는 방법은 정수와 실수로 나뉩니다. 이 레슨에서는 부호 있는 정수의 다양한 표현 방식(부호-크기, 1의 보수, 2의 보수)과 IEEE 754 부동소수점 표준을 학습합니다. 또한 정수 오버플로우와 부동소수점 정밀도 문제도 다룹니다.
난이도: ⭐⭐ (중급)
목차¶
1. 정수 표현 개요¶
부호 없는 정수 (Unsigned Integer)¶
n비트 부호 없는 정수:
- 범위: 0 ~ 2ⁿ - 1
- 모든 비트를 수의 크기 표현에 사용
8비트 예시:
┌─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┐
│ 2⁷ │ 2⁶ │ 2⁵ │ 2⁴ │ 2³ │ 2² │ 2¹ │ 2⁰ │
│ 128 │ 64 │ 32 │ 16 │ 8 │ 4 │ 2 │ 1 │
└─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┘
00000000 = 0
11111111 = 255
범위: 0 ~ 255 (256개의 값)
부호 있는 정수 표현 방법¶
┌─────────────────────────────────────────────────────────────┐
│ 부호 있는 정수 표현 방법 비교 │
├───────────────┬───────────────────────────────────────────────┤
│ 방법 │ 특징 │
├───────────────┼───────────────────────────────────────────────┤
│ 부호-크기 │ MSB가 부호, 나머지가 크기 │
│ (Sign-Mag) │ +0과 -0 존재, 연산 복잡 │
├───────────────┼───────────────────────────────────────────────┤
│ 1의 보수 │ 음수 = 양수의 비트 반전 │
│ │ +0과 -0 존재 │
├───────────────┼───────────────────────────────────────────────┤
│ 2의 보수 │ 음수 = 1의 보수 + 1 │
│ │ 0이 유일, 연산 효율적 (현대 표준) │
└───────────────┴───────────────────────────────────────────────┘
2. 부호-크기 표현¶
구조¶
부호-크기 표현 (Sign-Magnitude):
n비트에서:
┌─────────┬──────────────────────────────────────────┐
│ 부호 │ 크기 │
│ (1비트)│ (n-1 비트) │
└─────────┴──────────────────────────────────────────┘
↓
0 = 양수
1 = 음수
8비트 예시:
+45 = 0 0101101
↑ └─────┘
부호 크기(45)
-45 = 1 0101101
↑ └─────┘
부호 크기(45)
장단점¶
┌─────────────────────────────────────────────────────────────┐
│ 부호-크기 표현의 특성 │
├─────────────────────────────────────────────────────────────┤
│ 장점: │
│ - 직관적이고 이해하기 쉬움 │
│ - 부호 확인이 간단 (MSB만 확인) │
│ - 절댓값 계산이 쉬움 │
│ │
│ 단점: │
│ - +0 (00000000)과 -0 (10000000) 두 가지 0 존재 │
│ - 덧셈/뺄셈 연산이 복잡 (부호 비교 필요) │
│ - 하드웨어 구현이 복잡 │
├─────────────────────────────────────────────────────────────┤
│ n비트 범위: -(2^(n-1) - 1) ~ +(2^(n-1) - 1) │
│ 8비트 범위: -127 ~ +127 (255개 값, 0이 2개) │
└─────────────────────────────────────────────────────────────┘
8비트 부호-크기 표현 예시¶
┌──────────┬────────────┬──────────┐
│ 10진수 │ 이진수 │ 설명 │
├──────────┼────────────┼──────────┤
│ +127 │ 01111111 │ 최댓값 │
│ +45 │ 00101101 │ │
│ +1 │ 00000001 │ │
│ +0 │ 00000000 │ 양의 0 │
│ -0 │ 10000000 │ 음의 0 │
│ -1 │ 10000001 │ │
│ -45 │ 10101101 │ │
│ -127 │ 11111111 │ 최솟값 │
└──────────┴────────────┴──────────┘
3. 1의 보수 표현¶
구조¶
1의 보수 표현 (One's Complement):
양수: 그대로 표현
음수: 모든 비트를 반전
8비트 예시:
+45 = 00101101
-45 = 11010010 (모든 비트 반전)
↓↓↓↓↓↓↓↓
00101101 → 11010010
비트 반전 과정¶
양수 → 음수 변환:
+45 = 0 0 1 0 1 1 0 1
↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ (각 비트 반전)
-45 = 1 1 0 1 0 0 1 0
검증:
00101101 = 45
+ 11010010 = 210
────────────
11111111 = 255 = 2⁸ - 1 ✓
1의 보수 특성: N + (-N) = 2ⁿ - 1
1의 보수 덧셈¶
1의 보수 덧셈의 특징: 순환 자리올림 (End-around carry)
예시: 4비트에서 5 + (-3)
5 = 0101
-3 = 1100 (3의 1의 보수)
0 1 0 1 (+5)
+ 1 1 0 0 (-3)
─────────
1 0 0 0 1
↑
순환 자리올림 (다시 더함)
0 0 0 1
+ 1
─────────
0 0 1 0 = +2 ✓
0의 표현 문제¶
1의 보수에서 0의 표현:
+0 = 00000000
-0 = 11111111 (0의 1의 보수)
두 값 모두 0을 의미하지만 비트 패턴이 다름
→ 비교 연산, 분기 처리 시 추가 로직 필요
4. 2의 보수 표현¶
구조¶
2의 보수 표현 (Two's Complement):
양수: 그대로 표현
음수: 1의 보수 + 1
8비트 예시:
+45 = 00101101
-45 계산:
1) 1의 보수: 11010010
2) +1: 11010011 ← -45의 2의 보수 표현
2의 보수 계산 방법¶
방법 1: 1의 보수 + 1
45 = 00101101
↓↓↓↓↓↓↓↓ 반전
11010010
+ 1
────────
-45 = 11010011
방법 2: 오른쪽에서 첫 1까지 유지, 나머지 반전
45 = 0 0 1 0 1 1 0 1
└───┘ 유지
1 1 0 1 0 1 0 1
└─────┘ 반전
-45 = 1 1 0 1 0 0 1 1
방법 3: 2ⁿ - N
-45 = 256 - 45 = 211 = 11010011
2의 보수의 장점¶
┌─────────────────────────────────────────────────────────────┐
│ 2의 보수의 장점 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1. 0의 표현이 유일함 │
│ 00000000 만이 0을 표현 │
│ -0 = 2의 보수(00000000) = 00000000 (자기 자신) │
│ │
│ 2. 덧셈/뺄셈 연산이 동일 │
│ A - B = A + (-B) = A + (B의 2의 보수) │
│ │
│ 3. 순환 자리올림 불필요 │
│ 최상위 자리올림은 단순히 무시 │
│ │
│ 4. 하드웨어 구현이 단순 │
│ 하나의 가산기로 덧셈과 뺄셈 모두 처리 │
│ │
│ 5. 비대칭 범위 (하나 더 많은 음수) │
│ n비트: -2^(n-1) ~ +2^(n-1) - 1 │
│ 8비트: -128 ~ +127 │
│ │
└─────────────────────────────────────────────────────────────┘
2의 보수 덧셈/뺄셈¶
예시 1: 양수 + 양수 (8비트)
45 + 30 = 75
00101101 (+45)
+ 00011110 (+30)
──────────
01001011 (+75) ✓
예시 2: 양수 + 음수
45 + (-30) = 15
-30의 2의 보수: 11100010
00101101 (+45)
+ 11100010 (-30)
──────────
1 00001111
↑
캐리 무시
결과: 00001111 = +15 ✓
예시 3: 음수 + 음수
-45 + (-30) = -75
-45 = 11010011
-30 = 11100010
11010011 (-45)
+ 11100010 (-30)
──────────
1 10110101
↑
캐리 무시
결과: 10110101
양수로 변환: 01001011 = 75
따라서: -75 ✓
8비트 2의 보수 숫자표¶
┌──────────┬────────────┬────────────────────────────────────┐
│ 10진수 │ 이진수 │ 설명 │
├──────────┼────────────┼────────────────────────────────────┤
│ +127 │ 01111111 │ 양수 최댓값 │
│ +126 │ 01111110 │ │
│ ... │ ... │ │
│ +2 │ 00000010 │ │
│ +1 │ 00000001 │ │
│ 0 │ 00000000 │ 유일한 0 │
│ -1 │ 11111111 │ 모든 비트가 1 │
│ -2 │ 11111110 │ │
│ ... │ ... │ │
│ -127 │ 10000001 │ │
│ -128 │ 10000000 │ 음수 최솟값 (비대칭) │
└──────────┴────────────┴────────────────────────────────────┘
2의 보수 수 직선:
-128 -127 -1 0 1 126 127
│ │ │ │ │ │ │
└─────┴──── ─────┴───┴───┴───────────┴────┘
10000000 11111111 00000000 00000001 01111111
세 방식 비교¶
4비트 표현 비교:
┌────────┬────────────┬────────────┬────────────┐
│ 10진수 │ 부호-크기 │ 1의 보수 │ 2의 보수 │
├────────┼────────────┼────────────┼────────────┤
│ +7 │ 0111 │ 0111 │ 0111 │
│ +6 │ 0110 │ 0110 │ 0110 │
│ +5 │ 0101 │ 0101 │ 0101 │
│ +4 │ 0100 │ 0100 │ 0100 │
│ +3 │ 0011 │ 0011 │ 0011 │
│ +2 │ 0010 │ 0010 │ 0010 │
│ +1 │ 0001 │ 0001 │ 0001 │
│ +0 │ 0000 │ 0000 │ 0000 │
│ -0 │ 1000 │ 1111 │ 없음 │
│ -1 │ 1001 │ 1110 │ 1111 │
│ -2 │ 1010 │ 1101 │ 1110 │
│ -3 │ 1011 │ 1100 │ 1101 │
│ -4 │ 1100 │ 1011 │ 1100 │
│ -5 │ 1101 │ 1010 │ 1011 │
│ -6 │ 1110 │ 1001 │ 1010 │
│ -7 │ 1111 │ 1000 │ 1001 │
│ -8 │ 없음 │ 없음 │ 1000 │
└────────┴────────────┴────────────┴────────────┘
범위:
부호-크기: -7 ~ +7 (15개, 0이 2개)
1의 보수: -7 ~ +7 (15개, 0이 2개)
2의 보수: -8 ~ +7 (16개, 0이 1개)
5. 정수 오버플로우¶
오버플로우란?¶
┌─────────────────────────────────────────────────────────────┐
│ 오버플로우 (Overflow) │
├─────────────────────────────────────────────────────────────┤
│ 연산 결과가 표현 가능한 범위를 초과하는 현상 │
│ │
│ 8비트 2의 보수 예시: │
│ - 범위: -128 ~ +127 │
│ - 127 + 1 = ? (범위 초과!) │
└─────────────────────────────────────────────────────────────┘
양수 오버플로우:
01111111 (+127)
+ 00000001 (+1)
──────────
10000000 (-128) ← 예상치 못한 음수!
음수 오버플로우 (언더플로우):
10000000 (-128)
- 00000001 (+1)
──────────
01111111 (+127) ← 예상치 못한 양수!
오버플로우 검출¶
부호 있는 정수의 오버플로우 검출 조건:
1. 양수 + 양수 = 음수 → 오버플로우
2. 음수 + 음수 = 양수 → 오버플로우
3. 양수 + 음수 → 오버플로우 불가능
┌─────────────────────────────────────────────────────────────┐
│ 오버플로우 검출 (V 플래그) │
├─────────────────────────────────────────────────────────────┤
│ V = C_in(MSB) XOR C_out(MSB) │
│ │
│ C_in: MSB로의 캐리 입력 │
│ C_out: MSB에서의 캐리 출력 │
│ │
│ V = 1 → 오버플로우 발생 │
│ V = 0 → 오버플로우 없음 │
└─────────────────────────────────────────────────────────────┘
예시: 127 + 1 (8비트)
01111111
+ 00000001
──────────
C_in → 1 (bit 6에서 bit 7로 캐리)
C_out → 0 (bit 7에서 외부로 캐리 없음)
V = 1 XOR 0 = 1 → 오버플로우!
프로그래밍에서의 오버플로우¶
// C 언어 예시
#include <stdio.h>
#include <limits.h>
int main() {
// 부호 있는 정수 오버플로우 (정의되지 않은 동작!)
int max = INT_MAX; // 2147483647
printf("%d + 1 = %d\n", max, max + 1); // 예측 불가
// 부호 없는 정수 오버플로우 (정의된 동작, 순환)
unsigned int umax = UINT_MAX; // 4294967295
printf("%u + 1 = %u\n", umax, umax + 1); // 0
return 0;
}
# Python 예시 - 임의 정밀도 정수
a = 2**63 - 1 # 9223372036854775807
b = a + 1 # 9223372036854775808 (오버플로우 없음!)
# Python은 자동으로 큰 정수를 처리
huge = 10**100
print(huge) # 정상 출력
오버플로우 방지 전략¶
┌─────────────────────────────────────────────────────────────┐
│ 오버플로우 방지 전략 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1. 연산 전 검사 │
│ if (a > 0 && b > INT_MAX - a) → 오버플로우 발생 예정 │
│ │
│ 2. 더 큰 자료형 사용 │
│ int 대신 long, long 대신 long long │
│ │
│ 3. 임의 정밀도 라이브러리 사용 │
│ Python, Java BigInteger, GMP 등 │
│ │
│ 4. 컴파일러 옵션 활용 │
│ -ftrapv (GCC): 오버플로우 시 프로그램 중단 │
│ │
│ 5. 안전한 정수 연산 라이브러리 │
│ SafeInt (C++), checked_add (Rust) │
│ │
└─────────────────────────────────────────────────────────────┘
6. 고정소수점 표현¶
고정소수점 개념¶
고정소수점 (Fixed-Point):
소수점의 위치가 고정되어 있는 표현 방식
Q 표기법: Qm.n
- m: 정수부 비트 수
- n: 소수부 비트 수
- 총 비트 = m + n (부호 비트 제외 시) 또는 1 + m + n (부호 포함)
8비트 Q3.4 예시 (부호 없음):
┌───────────────────────────────────────┐
│ 정수부 (3비트) │ 소수부 (4비트) │
└───────────────────────────────────────┘
2² 2¹ 2⁰ . 2⁻¹ 2⁻² 2⁻³ 2⁻⁴
4 2 1 . 0.5 0.25 0.125 0.0625
예: 01011010 in Q3.4
= 0×4 + 1×2 + 0×1 + 1×0.5 + 1×0.25 + 0×0.125 + 1×0.0625 + 0×0.03125
= 2 + 0.5 + 0.25 + 0.0625
= 2.8125
고정소수점 연산¶
고정소수점 덧셈/뺄셈:
- 동일한 Q 형식이면 정수처럼 연산
고정소수점 곱셈:
- 결과의 소수부 비트 = 두 피연산자 소수부 비트의 합
- 오버플로우 방지를 위해 결과 비트 수 증가 필요
Q4.4 × Q4.4 = Q8.8 (8비트 × 8비트 = 16비트)
예시:
2.5 × 1.5 (Q4.4)
2.5 = 00101000 (0010.1000)
1.5 = 00011000 (0001.1000)
곱셈 결과: 00101000 × 00011000 = 0000001011100000 (Q8.8)
= 3.75 (0000001111.00000000)
7. IEEE 754 부동소수점¶
부동소수점 개념¶
부동소수점 (Floating-Point):
소수점의 위치가 유동적인 표현 방식
과학적 표기법과 유사:
6.022 × 10²³ (아보가드로 수)
↑ ↑ ↑
가수 기수 지수
이진 부동소수점:
1.01011 × 2⁵
↑ ↑
가수 지수
IEEE 754 형식¶
IEEE 754 구조:
┌─────────┬────────────────────┬───────────────────────────────┐
│ 부호 │ 지수 │ 가수 │
│ (Sign) │ (Exponent) │ (Mantissa) │
│ 1 bit │ E bits │ M bits │
└─────────┴────────────────────┴───────────────────────────────┘
값 = (-1)^S × 1.M × 2^(E - bias)
┌───────────────┬───────────┬───────────┬───────────┬────────────┐
│ 형식 │ 총 비트 │ 지수(E) │ 가수(M) │ Bias │
├───────────────┼───────────┼───────────┼───────────┼────────────┤
│ 단정밀도 │ 32 │ 8 │ 23 │ 127 │
│ (Single) │ │ │ │ │
├───────────────┼───────────┼───────────┼───────────┼────────────┤
│ 배정밀도 │ 64 │ 11 │ 52 │ 1023 │
│ (Double) │ │ │ │ │
├───────────────┼───────────┼───────────┼───────────┼────────────┤
│ 반정밀도 │ 16 │ 5 │ 10 │ 15 │
│ (Half) │ │ │ │ │
└───────────────┴───────────┴───────────┴───────────┴────────────┘
단정밀도 (32비트) 상세¶
32비트 단정밀도 (float):
31 30 23 22 0
┌───┬────────┬───────────────────────┐
│ S │ E │ M │
│ 1 │ 8 │ 23 │
└───┴────────┴───────────────────────┘
값 = (-1)^S × 1.M × 2^(E - 127)
예시: -6.625를 단정밀도로 변환
1단계: 이진수 변환
6 = 110₂
0.625 = 0.101₂
6.625 = 110.101₂
2단계: 정규화
110.101 = 1.10101 × 2²
3단계: 각 필드 결정
S = 1 (음수)
E = 2 + 127 = 129 = 10000001₂
M = 10101000000000000000000 (소수점 아래만, 23비트로 패딩)
결과: 1 10000001 10101000000000000000000
= 0xC0D40000
배정밀도 (64비트) 상세¶
64비트 배정밀도 (double):
63 62 52 51 0
┌───┬─────────────┬──────────────────────────────────────┐
│ S │ E │ M │
│ 1 │ 11 │ 52 │
└───┴─────────────┴──────────────────────────────────────┘
값 = (-1)^S × 1.M × 2^(E - 1023)
범위 비교:
┌─────────────┬───────────────────────────────────────────────┐
│ 형식 │ 범위 │
├─────────────┼───────────────────────────────────────────────┤
│ 단정밀도 │ ±1.18×10⁻³⁸ ~ ±3.4×10³⁸ │
│ (float) │ 유효숫자 약 7자리 │
├─────────────┼───────────────────────────────────────────────┤
│ 배정밀도 │ ±2.23×10⁻³⁰⁸ ~ ±1.8×10³⁰⁸ │
│ (double) │ 유효숫자 약 15-16자리 │
└─────────────┴───────────────────────────────────────────────┘
특수값¶
IEEE 754 특수값:
┌───────────────────────────────────────────────────────────────┐
│ 특수값 표현 │
├─────────┬──────────────┬─────────────┬────────────────────────┤
│ 값 │ 지수(E) │ 가수(M) │ 의미 │
├─────────┼──────────────┼─────────────┼────────────────────────┤
│ +0 │ 00000000 │ 000...0 │ 양의 영 │
│ -0 │ 00000000 │ 000...0 │ 음의 영 (S=1) │
├─────────┼──────────────┼─────────────┼────────────────────────┤
│ +∞ │ 11111111 │ 000...0 │ 양의 무한대 │
│ -∞ │ 11111111 │ 000...0 │ 음의 무한대 (S=1) │
├─────────┼──────────────┼─────────────┼────────────────────────┤
│ NaN │ 11111111 │ ≠ 0 │ Not a Number │
│ │ │ │ (0/0, ∞-∞ 등) │
├─────────┼──────────────┼─────────────┼────────────────────────┤
│ 비정규화│ 00000000 │ ≠ 0 │ 매우 작은 수 │
│ (Denorm)│ │ │ 0.M × 2^(-126) │
└─────────┴──────────────┴─────────────┴────────────────────────┘
NaN의 특성:
- NaN ≠ NaN (자기 자신과도 같지 않음)
- NaN과의 모든 비교는 false
- NaN과의 모든 연산 결과는 NaN
부동소수점 연산¶
덧셈 과정:
1. 지수 정렬 (작은 지수를 큰 지수에 맞춤)
2. 가수 덧셈
3. 정규화
4. 반올림
예시: 1.5 + 0.25 (단정밀도)
1.5 = 1.1 × 2⁰
0.25 = 1.0 × 2⁻²
지수 정렬:
0.25 → 0.01 × 2⁰
덧셈:
1.10
+ 0.01
──────
1.11 × 2⁰ = 1.75 ✓
곱셈 과정:
1. 부호 결정 (XOR)
2. 지수 덧셈 (bias 조정)
3. 가수 곱셈
4. 정규화
5. 반올림
예시: 1.5 × 2.0
1.5 = 1.1 × 2⁰
2.0 = 1.0 × 2¹
부호: 양수 × 양수 = 양수
지수: 0 + 1 = 1
가수: 1.1 × 1.0 = 1.1
결과: 1.1 × 2¹ = 3.0 ✓
8. 부동소수점 정밀도 문제¶
표현 오류¶
┌─────────────────────────────────────────────────────────────┐
│ 표현 오류 문제 │
├─────────────────────────────────────────────────────────────┤
│ 많은 십진 소수가 이진수로 정확히 표현되지 않음 │
│ │
│ 예: 0.1 (십진수) │
│ = 0.0001100110011001100110011... (이진수, 무한 반복) │
│ │
│ 저장 시 잘리므로 오차 발생 │
└─────────────────────────────────────────────────────────────┘
정확히 표현 가능한 수:
- 2의 거듭제곱의 합으로 표현 가능한 수
- 예: 0.5 (2⁻¹), 0.25 (2⁻²), 0.75 (2⁻¹ + 2⁻²)
정확히 표현 불가능한 수:
- 대부분의 십진 소수
- 예: 0.1, 0.2, 0.3 등
코드 예시¶
# Python 예시
>>> 0.1 + 0.2
0.30000000000000004
>>> 0.1 + 0.2 == 0.3
False
>>> format(0.1, '.20f')
'0.10000000000000000555'
>>> format(0.2, '.20f')
'0.20000000000000001110'
>>> format(0.1 + 0.2, '.20f')
'0.30000000000000004441'
// C 예시
#include <stdio.h>
int main() {
float a = 0.1f;
float sum = 0.0f;
for (int i = 0; i < 10; i++) {
sum += a;
}
printf("0.1 * 10 = %.10f\n", sum);
// 출력: 0.1 * 10 = 1.0000001192
// 예상: 1.0000000000
return 0;
}
정밀도 손실 상황¶
┌─────────────────────────────────────────────────────────────┐
│ 정밀도 손실 상황 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1. 큰 수와 작은 수의 덧셈 │
│ 1000000.0 + 0.0000001 ≈ 1000000.0 │
│ (작은 수가 유효숫자 범위 밖) │
│ │
│ 2. 비슷한 크기의 수 뺄셈 (Catastrophic Cancellation) │
│ 1.0000001 - 1.0000000 = 0.0000001 │
│ 유효숫자가 급격히 감소 │
│ │
│ 3. 반복 연산 │
│ 작은 오차가 누적되어 큰 오차로 │
│ │
│ 4. 무한 루프 위험 │
│ for (float x = 0.0f; x != 1.0f; x += 0.1f) │
│ → 영원히 종료되지 않을 수 있음! │
│ │
└─────────────────────────────────────────────────────────────┘
해결 방법¶
┌─────────────────────────────────────────────────────────────┐
│ 정밀도 문제 해결 방법 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1. 허용 오차(epsilon) 사용 │
│ |a - b| < epsilon 대신 a == b 사용 │
│ │
│ 2. 정수 연산 사용 │
│ 금액: 1.99달러 → 199센트로 저장 │
│ │
│ 3. Decimal 타입 사용 │
│ Python: from decimal import Decimal │
│ Java: BigDecimal │
│ │
│ 4. 연산 순서 최적화 │
│ 작은 수끼리 먼저 더한 후 큰 수와 연산 │
│ │
│ 5. Kahan Summation (보상 덧셈) │
│ 누적 오차를 추적하여 보정 │
│ │
└─────────────────────────────────────────────────────────────┘
허용 오차 비교 예시¶
# Python - epsilon 비교
import math
def float_equals(a, b, rel_tol=1e-9, abs_tol=1e-9):
"""부동소수점 비교"""
return abs(a - b) <= max(rel_tol * max(abs(a), abs(b)), abs_tol)
# 또는 Python 3.5+의 math.isclose 사용
print(math.isclose(0.1 + 0.2, 0.3)) # True
# Decimal 사용
from decimal import Decimal, getcontext
getcontext().prec = 50 # 정밀도 설정
a = Decimal('0.1')
b = Decimal('0.2')
c = Decimal('0.3')
print(a + b == c) # True
// C - epsilon 비교
#include <math.h>
#include <float.h>
int float_equals(float a, float b) {
return fabs(a - b) < FLT_EPSILON * fmax(fabs(a), fabs(b));
}
int double_equals(double a, double b) {
return fabs(a - b) < DBL_EPSILON * fmax(fabs(a), fabs(b));
}
9. 연습 문제¶
기초 문제¶
1. 8비트 2의 보수로 다음 수를 표현하시오. - (a) +50 - (b) -50 - (c) -1 - (d) -128
2. 다음 8비트 2의 보수 값을 10진수로 변환하시오. - (a) 01100100 - (b) 11001110 - (c) 10000000 - (d) 11111111
3. 다음 연산의 결과를 8비트 2의 보수로 계산하시오. - (a) 45 + 30 - (b) 45 - 30 - (c) -45 - 30 - (d) -1 + 1
IEEE 754 문제¶
4. 다음 10진수를 IEEE 754 단정밀도(32비트)로 변환하시오. - (a) 5.75 - (b) -0.375 - (c) 1.0
5. 다음 IEEE 754 단정밀도 비트 패턴을 10진수로 변환하시오. - (a) 0 10000001 01000000000000000000000 - (b) 1 01111111 00000000000000000000000 - (c) 0 00000000 00000000000000000000000
심화 문제¶
6. 다음 8비트 2의 보수 연산에서 오버플로우가 발생하는지 확인하시오. - (a) 01111111 + 00000001 - (b) 10000000 + 11111111 - (c) 01000000 + 01000000
7. 왜 0.1 + 0.2가 정확히 0.3이 되지 않는지 설명하시오.
8. Kahan Summation 알고리즘을 설명하고, 일반 덧셈과 비교하여 장점을 설명하시오.
9. IEEE 754에서 +0과 -0이 모두 존재하는 이유와 그 차이점을 설명하시오.
10. 다음 코드의 문제점과 해결 방법을 설명하시오.
float sum = 0.0f;
for (int i = 0; i < 1000000; i++) {
sum += 0.0001f;
}
printf("Expected: 100.0, Actual: %f\n", sum);
정답
**1. 8비트 2의 보수 표현** - (a) +50 = 00110010 - (b) -50 = 11001110 (50의 2의 보수) - (c) -1 = 11111111 - (d) -128 = 10000000 **2. 2의 보수 → 10진수** - (a) 01100100 = +100 (MSB가 0이므로 양수) - (b) 11001110 = -50 (MSB가 1이므로 음수, 보수 = 00110010 = 50) - (c) 10000000 = -128 - (d) 11111111 = -1 **3. 2의 보수 연산** - (a) 45 + 30 = 00101101 + 00011110 = 01001011 = +75 - (b) 45 - 30 = 00101101 + 11100010 = 00001111 = +15 - (c) -45 - 30 = 11010011 + 11100010 = 10110101 = -75 - (d) -1 + 1 = 11111111 + 00000001 = 00000000 = 0 **4. IEEE 754 단정밀도 변환** - (a) 5.75 = 101.11 = 1.0111 × 2² → 0 10000001 01110000000000000000000 - (b) -0.375 = -0.011 = -1.1 × 2⁻² → 1 01111101 10000000000000000000000 - (c) 1.0 = 1.0 × 2⁰ → 0 01111111 00000000000000000000000 **5. IEEE 754 → 10진수** - (a) S=0, E=129, M=0.25 → +1.25 × 2² = +5.0 - (b) S=1, E=127, M=0 → -1.0 × 2⁰ = -1.0 - (c) S=0, E=0, M=0 → +0.0 **6. 오버플로우 검출** - (a) 01111111 + 00000001 = 10000000 → 오버플로우! (양수+양수=음수) - (b) 10000000 + 11111111 = 01111111 → 오버플로우! (음수+음수=양수) - (c) 01000000 + 01000000 = 10000000 → 오버플로우! (64+64=-128) **7.** 0.1과 0.2는 이진 부동소수점으로 정확히 표현되지 않습니다. 둘 다 무한 반복 소수이므로 잘려서 저장되고, 그 오차가 합산됩니다. **8.** Kahan Summation은 각 덧셈에서 발생하는 반올림 오차를 별도 변수에 추적하여 다음 덧셈에서 보정합니다. 이를 통해 많은 수를 더할 때 오차 누적을 최소화합니다. **9.** +0과 -0은 비트 패턴은 다르지만 비교 시 같습니다. -0은 매우 작은 음수가 언더플로우될 때 발생하며, 1/+0 = +∞, 1/-0 = -∞로 부호 정보를 보존합니다. **10.** 0.0001f는 이진수로 정확히 표현되지 않아 오차가 발생하고, 100만 번 반복하면 오차가 누적됩니다. 해결: double 사용, Kahan Summation, 또는 정수 연산 후 나누기.다음 단계¶
- 04_Logic_Gates.md - 기본 논리 게이트와 불 대수
참고 자료¶
- IEEE 754-2019 Standard for Floating-Point Arithmetic
- What Every Computer Scientist Should Know About Floating-Point Arithmetic (Goldberg)
- Computer Organization and Design (Patterson & Hennessy)
- Float Toy - Interactive IEEE 754 Visualization
- Floating Point Guide