정수와 실수 표현

정수와 실수 표현

개요

컴퓨터에서 수를 표현하는 방법은 정수와 실수로 나뉩니다. 이 레슨에서는 부호 있는 정수의 다양한 표현 방식(부호-크기, 1의 보수, 2의 보수)과 IEEE 754 부동소수점 표준을 학습합니다. 또한 정수 오버플로우와 부동소수점 정밀도 문제도 다룹니다.

난이도: ⭐⭐ (중급)


목차

  1. 정수 표현 개요
  2. 부호-크기 표현
  3. 1의 보수 표현
  4. 2의 보수 표현
  5. 정수 오버플로우
  6. 고정소수점 표현
  7. IEEE 754 부동소수점
  8. 부동소수점 정밀도 문제
  9. 연습 문제

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, 또는 정수 연산 후 나누기.

다음 단계


참고 자료

to navigate between lessons