Real Number

실수Real Number

부동소수점Floating-Point

부동 소수점은 표현 범위는 넓지만 정밀도는 낮다.

단정도Single-precision

단정도는 실수를 32비트(4바이트)로 표현하며 부호 1비트, 지수부 8비트, 가수부 23비트로 구성된다

배정도Double-precision

배정도는 실수를 64비트(8바이트)로 표현하며 부호 1비트, 지수부 11비트, 가수부 52비트로 구성된다. 가수부(실수를 표현하는 데 사용하는 비트)수가 단정도보다 두 배 많은 만큼 정밀도가 높다.

>>>import sys
>>>sys.float_info
sys.float_info(max=1.7976931348623157e+308, max_exp=1024, max_10_exp=308, min=2.2250738585072014e-308, min_exp=-1021, min_10_exp=-307, dig=15, mant_dig=53, epsilon=2.220446049250313e-16, radix=2, rounds=1)

실수 자료형

$$\pm1.man \times 2^{exp-bias}$$

위 수식은 실수 자료형을 표현한 수식이다.
\(1.man\)은 가수(mantissa, fraction), 2는 밑수, \(exp-bias\)는 지수를 의미한다. 이 식을 활용해 7.75라는 10진수 실수를 1바이트 부동소수점으로 표현해보자.

  1. 10진수 실수를 2진수 실수로 바꾸기
    정수를 바꾸는 방법과 크게 다르지 않지만 \(2^{-1}, 2^{-2}\)는 각각 1/2, 4/1을 나타낸다.
    \(\begin{matrix} 7.75 &=& 4+2+1+0.5+0.25\\ &=&2^2 + 2^1 + 2^0 + 2^{-1} 2^{-2}\\ &=&111.11 \end{matrix}\)
  2. 정규화
    정규화란 소수점 왼쪽에 위치한 가수 부분을 밑수보다 작은 자연수가 되도록 만드는 작업이다. 2진수의 밑수는 2이며, 2보다 작은 자연수는 1 뿐이므로 111.11을 정규화 하면 다음과 같다.
    \(111.11=1.1111\times2^{2}\)
  3. 메모리 구조
    1바이트 부동소수점은 0(부호) 000(지수부) 0000(가수부) 형태로 구성되어있다. 지수부로 exp, 가수부로 man값을 저장하며, bias는 지수의 부호를 결정하는 데 쓴다.
    부동소수점의 지수부엔 0~15의 양수만 나타낼 수 있으며, 음수 지수를 표현하기 위해서 bias는 \(2^{n-1}-1\)의 식에 지수부의 비트수를 대입하면 구할 수 있다. \(1.1111\times2^2\)에서 실제 지수는 2이다. 실제 지수가 2라는 의미는 \(exp-bias\)가 2라는 것이므로 \(bias = 7, exp = 9\)가 된다. 최종적으로 나타나는 형태는 다음과 같다. 이때 가수부는 3비트만 할당되므로 뒷자리 1을 생략한다.
    \(0\ 1001\ 111 = 0100\ 1111 = 0x4f\)
  4. 정밀도 변환 과정에서 가수부 데이터 공간이 부족해 맨 뒤의 1을 누락하였으므로 0x4f는 7.75가 아닌 7.5를 표현하게 되며 0.25만큼 차이가 나므로 그만큼 정밀도가 떨어진다.
  5. 1바이트 부동소수점의 표현범위
표현할 수 있는 가장 작은 수(지수부 0001) 표현할 수 있는 가장 큰 수(지수부 1110)
\(1.000\times2^{-6}=0.015625\) \(1.111\times2^7=240\)

지수부 비트가 모두 0일 때와 모두 1일 때는 0.0, 정규화 불가능, 무한대, NaN이므로 제외한다.

정밀도

배정도 항목에서 살펴본 내용 중 엡실론(epsilon)은 1.0과 그 다음으로 표현 가능한 수 사이의 차를 이야기한다.

>>>sys.float_info.epsilon
2.220446049250313e-16

1.0을 배정도에 맞춰 표현하면 다음과 같다
\(1.0000 ....0000 \times 2^0(0:52개)\)
배정도에서 1.0 다음으로 표현할 수 있는 수는 다음과 같다.
\(1.0000....0001\times2^0(0:51개)\)
따라서 두 수의 차이는 다음과 같으며 그 수를 10진수로 표현하면 엡실론 값이 나온다.
\(1.0000....0001\times2^0(0:51개)\)
\(= 2.220446049250313\times10^{-16}\)
엡실론을 이용하면 그 실수 다음에 표현할 수 있는 수를 알아낼 수 있다.
예를 들어 9.25는 부동소수점 방식으로 \(1.00101\times2^3\)이다. 이 식에서 지수 부분만 떼어 엡실론을 곱하면 이 실수와 다음 표현 가능한 수 사이의 차를 구할 수 있다.

>>> import sys
>>> ep = sys.float_info.epsilon
>>> a = 9.25
>>> diff = (2**3)*ep
>>> diff
1.7763568394002505e-15
>>> b = a + diff
>>> b
9.250000000000002

만약 9.25에 diff보다 작은 값을 더하면 어떻게 될까?

>>> half = diff/2
>>> c = a + half
>>> a == c
True

a와 c는 다른 값이지만 같다고 나온다. 이는 diff보다 작은 값을 더한 수를 부동소수점 방식에서는 표현할 수 없기 때문이다. 정밀도가 떨어진다는 말과도 같다.




Enjoy Reading This Article?

Here are some more articles you might like to read next:

  • [CS231n]Exercise1.5 - Features
  • [CS231n]Exercise1.4 - Two Layer Net
  • [CS231n]Exercise1.3 - Softmax
  • [CS231n]Exercise1.2 - Support Vector Machine