수치해석

재료공학도를 위한 수치해석

1. 유한정밀도

  • 컴퓨터는 정보를 0과 1, 즉 이진수(binary number)로 저장한다.
  • 실수(real number)는 무한히 많은 자릿수를 가질 수 있지만 컴퓨터의 메모리는 한정되어 있으므로 정해진 비트 수까지만 저장할 수 있다(예: 32비트, 64비트).
  • 따라서 많은 실수는 정확한 값이 아니라 가까운 값으로 저장된다. 이를 컴퓨터가 유한정밀도(finite precision)를 갖는다고 표현한다.

2. 부동소수점 (floating point)

  • 부동소수점은 수를 부호(sign), 지수(exponent), 가수(fraction)로 나누어 이진수로 표현하는 방식이다.

  • 비교할 수 있는 다른 방식으로 고정소수점(fixed point)이 있다.

\[\text{값}=(-1)^s\times(1.f)_2\times2^{E-\text{bias}}\]

여기서 $s$는 부호 비트, $E$는 저장된 지수, $f$는 저장된 가수이다. 이 식은 정규화된 유한한 수(normalized finite number)에 적용된다. 0, 비정규수, 무한대(Infinity)와 NaN(Not a Number)은 별도의 규칙으로 표현한다.

2.1. 정수를 이진수로 나타내기

  • 10진수 정수를 이진수로 바꾸는 방법은 2로 나누어 몫과 나머지를 구하는 것이다.
  • 예를 들어, 10진수 13을 이진수로 바꾸어보자.

           몫    나머지
     ---------------------
     13 / 2 = 6 ... 1
     6 / 2  = 3 ... 0
     3 / 2  = 1 ... 1
     1 / 2  = 0 ... 1
    

    따라서 13의 이진수는

     1101
    

    다르게 표현하자면 \(13=\textcolor{red}{1}\times2^3+\textcolor{red}{1}\times2^2+\textcolor{red}{0}\times2^1+\textcolor{red}{1}\times2^0\)

2.2. 10진수 5.75 저장하기

  • 5 = 101 : $5=\textcolor{red}{1}\times2^2+\textcolor{red}{0}\times2^1+\textcolor{red}{1}\times2^0$
  • 0.75 = 0.11

0.75를 이진수로 만들기 위해서는 우선 2를 곱한다:

\[0.75\times 2 = 1.5\]

정수 부분 1을 기록하고, 나머지 소수 부분 0.5에 다시 2를 곱한다.

\[0.5\times 2 = 1.0\]

정수 부분 1을 기록하면 나머지가 0이므로 계산을 마친다. 따라서 $5.75_{10}=101.11_2$이다. 이 값을 정규화한 뒤 부호, 지수, 가수로 저장한다.

즉 \(5.75=\textcolor{red}{1}\times2^2+\textcolor{red}{0}\times2^1+\textcolor{red}{1}\times2^0+\textcolor{red}{1}\times2^{-1}+\textcolor{red}{1}\times2^{-2}\)

2.2.1. 정규화(normalization)

IEEE 754의 정규화된 이진 부동소수점 수는

\[1.\text{XXXX..} \times 2^n\]

형태로 바꾸어 저장한다. 따라서 이진수 101.11을 다음과 같이 변환한다.

\[101.11\rightarrow1.\green{0111}\times 2^{\red{2}}\]

과 같이 표현한다.

  • 가수(fraction) = $\green{0111}0000…$ (남는 비트는 0으로 채운다.)

  • 지수(exponent) = $\red{2}$

  • 밑이 2인 이유는 이진수를 사용하기 때문이다.

2.2.2. 부호

5.75는 양수이므로 부호 비트는 0이다.

2.2.3. 지수

IEEE 754에 의하여 지수를 바이어스(bias) 방식으로 저장한다.

  • 배정밀도(double precision)의 바이어스는 $\blue{1023}$이다.
  • 실제 지수는 $\red{2}$이다.
  • 저장되는 지수는 $\red{2}+\blue{1023}=1025$이다.
  • 바이어스를 적용한 1025를 이진수로 표현해보자.
        몫    나머지
---------------------
1025 / 2 = 512 ... 1
512 / 2  = 256 ... 0
256 / 2  = 128 ... 0
128 / 2  = 64  ... 0
64 / 2   = 32  ... 0
32 / 2   = 16  ... 0
16 / 2   = 8   ... 0
8 / 2    = 4   ... 0
4 / 2    = 2   ... 0
2 / 2    = 1   ... 0
1 / 2    = 0   ... 1

따라서 저장되는 지수는 10000000001(11비트)이다.

2.2.4. 가수부 (fraction)

정규화된 수 $1.0111\times2^2$에서 소수점 앞의 1은 항상 존재하므로 저장하지 않는다. 이를 숨겨진 비트(hidden bit)라고 한다. 따라서 0111 뒤를 0으로 채운 52비트가 가수에 저장된다.

  • 5.75IEEE 754 방식으로 저장하면 다음과 같다.

  • 부호: 0 (1비트)
  • 지수: 10000000001 (11비트)
  • 가수: 0111000000000000000000000000000000000000000000000000 (52비트)

따라서 전체 비트열은 다음과 같다.

0 10000000001 0111000000000000000000000000000000000000000000000000

2.3. 예제

2.3.1. 10진수 1.5 저장하기

  • 1=1
  • 0.5 = ?
\[0.5 \times 2 = 1.0\]

정수 부분 1이 나오고 나머지가 0이므로 $0.5_{10}=0.1_2$이다. 따라서

\[1.5_{10}=1.1_2\]

가 된다.

  • 정규화는?
\[1.\text{XXXX...} \times 2^n\]

으로 해야하므로,

\[1.1\rightarrow1.1 \times 2^0\]

가수와 지수를 구하면 다음과 같다.

  • 가수: $10000…$
  • 실제 지수: 0
  • 부호 비트: 0
  • 바이어스를 적용한 지수: $0+1023=1023$

1023을 이진수로 표현하면 다음과 같다.

1023/2=511 ... 1
511 /2=255 ... 1
255 /2=127 ... 1
127 /2=63  ... 1
63  /2=31  ... 1
31  /2=15  ... 1
15  /2=7   ... 1
7   /2=3  ... 1
3   /2=1  ... 1
1.  /2=0. ... 1

계산 결과는 1111111111(10비트)이지만 지수 필드는 11비트이므로 앞에 0을 붙인 01111111111로 저장한다.

  • 정규화된 수 $1.1\times2^0$에서 앞자리 1은 숨기고, 소수점 아래의 1 뒤에 0을 채운 52비트를 가수로 저장한다.

  • 부호: 0 (1비트)
  • 지수: 01111111111 (11비트)
  • 가수: 1000000000000000000000000000000000000000000000000000 (52비트)
0 01111111111 1000000000000000000000000000000000000000000000000000

2.3.2. 단정밀도(single precision)에서 0.5 저장하기

IEEE 754 단정밀도(single precision)는 총 32비트를 사용한다.

  • 부호: 1비트
  • 지수: 8비트
  • 가수: 23비트
  • 바이어스: $\color{blue}{127}$

10진수 0.5를 이진수로 나타내고 정규화하면

\[0.5_{10}=0.1_2=1.0_2\times2^{-1}\]

이다. 양수이므로 부호 비트는 0이다. 실제 지수는 $-1$이므로 저장되는 지수는

\[-1+127=126=01111110_2\]

이다. 정규화된 가수에서 숨겨진 1 뒤에는 모두 0만 있으므로 전체 비트열은

0 01111110 00000000000000000000000

이다. $0.5=2^{-1}$이므로 단정밀도에서도 반올림 오차 없이 정확히 저장할 수 있다.

단정밀도는 약 7자리, 배정밀도는 약 15~16자리의 10진 유효숫자를 표현할 수 있다.

2.3.3. 0.1을 저장한다면

10진 소수의 소수 부분에 2를 반복해서 곱하고, 각 결과의 정수 부분을 위에서부터 차례로 읽으면 이진수의 소수점 아래 자릿수를 구할 수 있다.

계산 순서 2를 곱한 결과 정수 부분 다음 계산에 사용할 소수 부분
1 $0.1\times2=0.2$ 0 0.2
2 $0.2\times2=0.4$ 0 0.4
3 $0.4\times2=0.8$ 0 0.8
4 $0.8\times2=1.6$ 1 0.6
5 $0.6\times2=1.2$ 1 0.2
6 $0.2\times2=0.4$ 0 0.4
7 $0.4\times2=0.8$ 0 0.8
8 $0.8\times2=1.6$ 1 0.6
9 $0.6\times2=1.2$ 1 0.2

정수 부분을 위에서부터 순서대로 읽으면

\[0.1_{10}=0.00011001100110011\ldots_2\]

가 된다. 5번째 계산이 끝난 뒤 소수 부분이 다시 0.2가 되어 2번째 계산과 같은 상태로 돌아간다. 따라서 0011이 계속 반복된다.

\[0.1_{10}=0.0\overline{0011}_2\]

이를 부동소수점 형식에 맞게 정규화하면 다음과 같다.

\[0.0001100110011\ldots_2 =1.100110011\ldots_2\times2^{-4}\]

하지만 가수에 사용할 수 있는 비트 수는 한정되어 있으므로 반복되는 모든 자릿수를 저장할 수 없다. 컴퓨터는 일정한 위치에서 이진수를 반올림하여 0.1에 가장 가까운 값을 저장한다. 따라서 Python에서 자릿수를 길게 출력하면 다음과 같이 보인다.

x = 0.1
print(f"{x:.20f}")
0.10000000000000000555

즉, 컴퓨터에 저장된 값은 수학적으로 정확한 $0.1$이 아니라 $0.1$에 매우 가까운 부동소수점 근삿값이다.

- \(0.1=\textcolor{red}{0}\times 2^{-1}+\textcolor{red}{0}\times 2^{-2}+\textcolor{red}{0}\times 2^{-3}+\textcolor{red}{1}\times 2^{-4}+\textcolor{red}{1}\times 2^{-5}+\cdots\)

2.3.4. 큰 수와 작은 수를 더할 때

\[10^{16}+1-10^{16}=?\]
print(1e16+1-1e16)
  • 연산 순서에 따라 결과가 달라질 수 있음
# 예제 1: 큰 수 + 작은 수
a = 1e16   # 매우 큰 수
b = 1.0    # 작은 수
c = -1e16  # 큰 음수

res1 = (a + b) + c
res2 = (a + c) + b

print("(a + b) + c =", res1)
print("(a + c) + b =", res2)

실행 결과는 다음과 같다.

(a + b) + c = 0.0
(a + c) + b = 1.0

첫 번째 계산에서는 $10^{16}$에 1을 더할 때 두 수의 크기 차이가 너무 커서 작은 수 1이 표현 정밀도의 한계로 사라진다. 두 번째 계산에서는 큰 양수와 큰 음수를 먼저 더한 뒤 1을 더하므로 정확한 결과를 얻는다. 따라서 컴퓨터의 부동소수점 덧셈에서는 연산 순서에 따라 결과가 달라질 수 있다.

2.3.5. 표현 범위와 정밀도 확인하기

NumPy의 finfo를 사용하면 부동소수점 자료형의 범위와 정밀도를 확인할 수 있다.

import numpy as np

for dtype in [np.float32, np.float64]:
    info = np.finfo(dtype)
    print(dtype)
    print("  epsilon =", info.eps)
    print("  smallest positive normal =", info.tiny)
    print("  largest finite value =", info.max)

여기서 eps는 1보다 큰 수 중 1과 가장 가까운 표현 가능한 수와 1의 차이이다.

eps = np.finfo(np.float64).eps

print(1.0 + eps == 1.0)
print(1.0 + eps/2 == 1.0)

일반적인 배정밀도 환경에서는 첫 번째 비교가 False, 두 번째 비교가 True로 출력된다. 이는 수를 구별할 수 있는 간격이 유한하며, 그 간격보다 작은 변화는 저장 과정에서 사라질 수 있음을 보여준다.

3. 연습 문제

강의에서 배운 내용을 확인하는 기초 문제이다. 계산 문제는 풀이 과정도 함께 작성하라.

3.1. 기본 개념

  1. 다음 빈칸을 채워라.

    • 컴퓨터는 모든 정보를 (    )과 (    )으로 저장한다.
    • 실수를 저장할 때는 부호, (    ), 가수로 나누어 저장한다.
    • 저장할 수 있는 비트 수가 정해져 있기 때문에 컴퓨터는 (    ) 정밀도를 갖는다.
  2. 다음 설명이 맞으면 O, 틀리면 X로 표시하라.

    1. 컴퓨터는 모든 실수를 정확하게 저장할 수 있다. (  )
    2. $0.5$는 이진수로 유한하게 표현할 수 있다. (  )
    3. $0.1$은 이진수로 표현하면 무한히 반복된다. (  )
    4. IEEE 754에서는 부호, 지수, 가수를 이용하여 부동소수점 수를 저장한다. (  )
  3. 64비트 배정밀도 부동소수점에서 각 부분이 사용하는 비트 수를 적어라.

    구분 비트 수
    부호  
    지수  
    가수  
    합계 64
  4. 양수와 음수의 부호 비트를 각각 적어라.

    • 양수의 부호 비트:     
    • 음수의 부호 비트:     

3.2. 이진수와 정규화

  1. 다음 이진수를 10진수로 바꾸어라.

    1. $10_2$
    2. $101_2$
    3. $11.1_2$

    소수점 아래 첫 번째 자리는 $2^{-1}=1/2$임을 이용하라.

  2. 다음 10진수를 이진수로 바꾸어라.

    1. $2$
    2. $3$
    3. $0.5$
    4. $2.5$
  3. 다음 이진수를 $1.\text{XXXX}\times2^n$ 형태로 정규화하고 지수 $n$을 구하라.

    1. $10.1_2$
    2. $101.0_2$
    3. $0.11_2$
  4. 10진수 $1.5$는 이진수로 $1.1_2$이다. 64비트 배정밀도로 저장할 때 다음 빈칸을 채워라.

    • 정규화된 표현: $1.1_2\times2^{(  )}$
    • 부호 비트:     
    • 실제 지수: $    $
    • 바이어스를 더한 지수: $    +1023=    $
    • 숨겨진 첫 번째 1을 제외한 가수의 첫 비트:     

3.3. Python 실습

  1. 다음 코드를 실행하고 출력 결과를 기록하라.

    print(0.5 + 0.25)
    print(0.1 + 0.2)
    print((0.5 + 0.25) == 0.75)
    print((0.1 + 0.2) == 0.3)
    

    두 비교 결과가 다른 이유를 유한정밀도와 관련지어 한 문장으로 설명하라.

  2. 다음 코드를 실행하여 연산 순서에 따른 결과를 비교하라.

    a = 1e16
    b = 1.0
    c = -1e16
    
    result1 = (a+b)+c
    result2 = (a+c)+b
    
    print(result1)
    print(result2)
    
    1. 수학적으로 $a+b+c$의 값은 얼마인가?
    2. result1result2의 값을 각각 기록하라.
    3. 두 결과가 달라지는 이유를 설명하라.
  3. Python이 저장한 $0.1$을 소수점 아래 20자리까지 출력해보자.

    x = 0.1
    print(f"{x:.20f}")
    

    출력된 값이 정확히 0.10000000000000000000인지 확인하고 관찰한 내용을 적어라.

  4. NumPy를 이용하여 32비트와 64비트 부동소수점 수를 만들어 비교하라.

    import numpy as np
    
    x32 = np.float32(1.0/3.0)
    x64 = np.float64(1.0/3.0)
    
    print(x32)
    print(x64)
    

    어느 자료형이 더 많은 자릿수를 표현하는지 답하라.