본문 바로가기

컴퓨터 기본 지식

컴퓨터의 실수 표현 방법

728x90
반응형

Goal

  • 실수를 표현하는 방법에 대한 이해
  • 실수 비교를 할 수 있다

실수(real number) : 수학에서 실직선 위에 점이나, 십진수의 나열로 표현한 수

 

컴퓨터에서 실수를 표현하기 위해서 다음과 같은 상황들이 고려 되어야 한다.

  1. 정수 부분과 소수 부분의 표현
  2. 두 실수 사이에는 무한한 다른 실수가 존재하기 때문에 적절한 근사치로 표현

우선 십진수의 실수 표현에 대해 알아보자

십진수 13.625 는 십진법(위치값 기수법 중 하나)으로 표현하면 다음과 같이 표현 될 수 있다.

$13.625_{(10)} = 1 \times 10^1 + 3 \times 10^0 + 6 \times 10^{-1} + 2 \times 10^{-2} + 5 \times 10^{-3}$

 

그렇다면 십진수로 표현된 실수를 이진수로는 어떻게 표현될까?

십진수 실수의 정수 부분에 대해서는 정수 표현 방법 과 동일하게 계산하면 된다. (2로 나눈 나머지의 역순으로 계산)

=> 13.125에서 13은 $1101_{(2)}$ 로 표현될 수 있다.

 

소수 부분도 정수 표현법과 동일하게 계산 하면 될까?

소수 부분 0.625에 대해서 위와 동일한 방식으로 적용하면 다음과 같은 이진수로 표시될 수 있다.

$0.625_{(10)} =0.1001110001_{(2)}$

이는 이진법으로 표기했을 때 0.625가 아닌 이상한 값을 갖게 된다.

 

그래서 다른 방법으로 계산 되어야 한다. 소수점을 계산 하는 방법은 다음과 같다.

자리 올림수 : 곱 연산에 의한 결과 값에서 정수 부분을 뜻한다. (0 또는 1값을 갖는다)

  1. 소수 부분이 0이면 종료
  2. 소수 부분이 0이 아니면 소수 * 2를 한다.
  3. 2의 결과 값에서 정수 부분을 출력 값으로 그대로 사용한다.
  4. 2의 결과 값에서 소수 부분은 다음 연산의 값으로 사용하고 위 과정을 반복한다.

쉽게 이해하면 그냥 2를 계속 곱해서 정수 부분을 그대로 출력하는 것이다. (역순으로 출력할 필요가 없다)

십진수 -> 이진수로의 변환 $0.625_{(10)} =0.101_{(2)} = 1 \times 2^{-1} + 0 \times 2^{-2} + 1 \times 2^{-3}$

 

 

자, 이로써 소수 부분을 표현할 수 있게 됐다.

하지만 아직 문제가 있다. 실수는 무한하다는 것이다.

한정된 컴퓨터 비트수로는 무한한 실수를 표현할 수 없다.

그래서 컴퓨터는 표현할 수 없는 수에 대해서는 근사한 값으로 표현한다.

 

실수 표현법

다음은 실수를 표현하는 표현법이다.

  • 고정 소수점 표기법(fixed point notation)
  • 부동 소수점 표기법(floating point notation)

*여기서 '부동'은 '고정되지 않고 움직인다는 의미로', 흔히 사용하는 '움직이지 않음'의 의미가 아니다.

 

둘의 가장 큰 차이는 소수점이 움직일 수 있는지 없는지이다.

고정 소수점 표기법(fixed point notation)

소수점이 고정되어 있는 실수 표현법

 

32비트 실수를 고정 소수점 방식으로 표현하면 다음과 같다.

고정 소수점은 부호, 정수부, 소수부를 표현하는 3부분으로 나뉘고 이 크기가 고정 되어있다.

이런 이유로 아주 큰수나 아주 작은 수를 표현하기에는 적합하지 않다. => 실수 표현 범위가 넓지 않다.

부동 소수점 표기법(floating point notation)

소수점의 위치가 변하는 실수 표기법

 

컴퓨터에서는 사용하는 부동 소수점 표기법은 IEEE 754 표현법을 따르며,

다음과 같은 형태로 표현된다.

 

  • 부호 비트(singed bit) : 최상위 1bit로 0(양수) 1(음수)의 값을 갖는다.
  • 지수(exponent) : 수의 표현 범위를 나타내며, 지수에 의해서 소수점이 이동한다.
  • 가수(significand, fraction, mantissa) : 양의 정수로 나타낸 근사치를 나타내며, 정밀도(precison)에 따라 다르다.

이를 수식으로 표현하면 다음과 같다.

$(-1)^s \times m \times b^e$

  • s(signed) : 부호
  • m(mantissa) : 가수
  • b(base / radix) : 밑, 또는 기수 (IEEE 754에서의 2와 10이 밑이 될 수 있다)
  • e(exponent) : 지수

ex) 십진수에 대한 부동 소수점 표기 예

176000 을 부동 소수점으로 표기해라

  1. $1.76 \times 10^{5}$ => 소수점을 오른쪽으로 5칸 움직여서 표현
  2. $17.6 \times 10^{4}$ => 소수점을 오른쪽으로 4칸 움직여서 표현
  3. $176000000 \times 10^{-3}$ => 소수점을 왼쪽으로 3칸 움직여서 표현

ex) 이진수에 대한 부동 소수점 표기 예

101.101을 부동 소수점으로 표기해라

  1. $1.01101 \times 2^{2}$ => 소수점을 오른쪽으로 2칸 움직여서 표현
  2. $10.1101 \times 2^{1}$ => 소수점을 오른쪽으로 1칸 움직여서 표현
  3. $101101 \times 2^{-3}$ => 소수점을 왼쪽으로 3칸 움직여서 표현

=> 지수값소수점이 이동하면서 큰 값과 작은 값을 표현할 수 있게 됨

 

하지만 위와 같이 표기하면 혼동이 있다. 같은 수를 서로 다른 부동 소수로 표기가 가능하다.

그래서 정규화된 표기법(normalized notation)을 사용한다.

(실수의)정규화(normalization) : 가수의 첫째 자리가 밑수보다 작은 한자리 자연수가 되도록 바꾸는 것을 정규화

 

ex) -0.4를 10진수로 2진수로 각각 정규화해라

1) -4를 10진수로 정규화

$-4 \times 10^{-1}$ => 10보다 작은 한자리 자연수( 4 < 10 )로 표현

(가수 부분이 40이나 0.4와 같이 표시된 부동 소수점은 정규화를 거치지 않은 부동 소수점 표기이다)

 

2) -4를 2진수로 정규화

$-1.6 \times 2^{-2}$ => 4는 2보다 큰 한자리 자연수이므로 가수부에 4가 올 수 없다. 그렇기 때문에 2진수에서 정규화를 거치면 가수부 첫째 수는 항상 1이 된다. 

(만약 가수부가 0이라면?? 이 경우는 정규화 자체를 거치지 않는다, 모든 비트가 0으로 표시된다.)

 

2진수의 정규화 IEEE754표기법

$±(1.xxx) \times 2^{(exponent-bias)}$

 

1.xxxxx는 가수부를 의미한다.

정규화를 거치면 정수부가 항상 1이 되는데, 이는 계산시 생략해서 표현하므로 hidden bit라고도 한다.

 

실수의 정밀도 (IEEE754 기준)

더보기

단정도 : 32-bit 실수 표현법

  • 부호부 : 1bit
  • 지수부 : 8bit
  • 가수부 : 23bit
  • 유효 숫자 : 6자리

배정도 : 64-bit 실수 표현법

  • 부호부 : 1bit
  • 지수부 : 11bit
  • 가수 : 52bit
  • 유효 숫자 : 15자리

정규화는 다음 과정을 거쳐서 진행된다.

  1. 실수의 부호를 부호bit로 표현한다. (0 은 +, 1은 -)
  2. 절대값을 이진법으로 표현한다. (정수 부분, 소수 부분을 그대로 표현)
  3. 정규화(normalize) 처리를 하여 산술표현 형식으로 만든다. $m \times b^e$ (m:가수, b:밑, e:지수)
  4. 위의 결과에서 가수(m)의 가장 앞의 1을 제외한 나머지를 가수부 bit에 채운다. (남는 bit는 0으로 채움)
  5. 지수(e)는 bias값을 더한 후 이진법으로 표현하여 지수부 bit에 채운다.

*bias : 지수값을 계산 하기 위한 조정값 (32bit 부동 소수점 표현법에서는 127값을 갖는다)

사용이유)

지수부는 양수/음수 모두 가능하기 때문에 이를 표현하는 특별한 방법을 사용하는데 IEEE 754에서는 bias 표현을 사용하여 양수와 음수를 표현한다.


단정도(32-bit) 형식에서 지수부는 8 bit로 구성되어있고 최소값은 -126이고 최대값은 127을 갖는다.

=> bias + e(지수) = 지수부(expoenent). -126 <= e <= 127

음수 : -126 ~ 0(127개), 양수 1~127(127) => 양수, 음수 모두 정확히 반을 사용할 수 있다.

 

expoenent 가 0인 경우와 255인 경우는 특수하게 처리 되므로 위와 같은 범위 값을 갖는다

 

참고) IEEE754표기법에서 특별하게 처리 되는 값들 (NaN, 무한대, 0)

더보기

다음은 일반적인 정규화 과정을 거치지 특별한 경우이다.

1) NaN (Not a number)

NaN 표현 방법

  1. 지수부 : 모든 bit가 1이다.
  2. 가수부 : 0아 아닌 값으로 채워져 있다 (NaN이 발생한 여러 경우를 구분하기 위한 수)
  3. 부호부 : 의미를 두지 않는다.

2) 무한대 (overflow 발생했을 때에 대한 처리)

무한대 표현 방법

  1. 지수부 : 모든 bit가 1이다.
  2. 가수부 : 모든 bit가 0이다.
  3. 부호부 : 음/양 무한대를 표시 하기 위한 구분자로 사용

3) Signed Zero

IEEE 754에서는 부호가 있는 0을 구분하고 있다.
단 if (x == 0)과 같은 작업에서 불확실한 결과를 낼 수 있기 때문에 -0 = +0 이라는 규칙을 정해놓았다.
부호 있는 0을 사용하게 되면 1 / -∞ < 1 / +∞ 와 같은 표현이 가능하게 된다.

Signed Zero 표현 방법 (부호 비트 뺴고 다 0으로 표현)

  1. 지수부 : 모든 bit가 0이다.
  2. 가수부 : 모든 bit가 0이다.
  3. 부호부 : 음/양 무한대를 표시 하기 위한 구분자로 사용

(C++에서 -0.0을 int형에 메모리복사를 하면 최대 음수값을 나타내고, 대입하면 0으로 표시된다. 신기하다!!)


실수 비교하기

부동 소수점 표기 방식 특성상 정확한 실수값을 표현할 수 없다.

예를 들어, [IEEE 754] 0.1 + 0.2 = 0.30000000000000004이다. 즉, 0.1 + 0.2 ≠ 0.3 이다.

따라서 두 실수값을 비교하기 위해서는 '같다', '다르다' 와 같은 비교가 아니라 '같다고 봐도 무방하다.'와 같은 방식으로 비교를 해야 한다. 이때 사용하는 비교 값이 epsilon이다.

 

epsilon : 같다고 판단할 만큼 작은 오차 범위를 표현할 때 사용하는 값

두 실수를 비교할 때, 같다고 판단할 오차값으로 사용된다.

 

epsilon을 사용하면 다음과 같은 실수 비교 함수를 만들 수 있다.

 

1. 절대 오차 범위 사용하기

구현 코드

bool absoluteEqual(double a, double b)
{
	return fabs(a-b) < 1e-10; // 여기에서는 1e-10이 epsilon으로 사용됐다.
}

 

위와 같은 방법으로 실수가 같은지 비교할 수 있지만, 안전한 방법은 아니다.

그 이유는 다음과 같다.

  • a, b의 값이 커짐에 따라 반올림되는 값 또한 커진다. (따라서 허용 오차 또한 커져야 한다)

 

그래서 다음과 같은 방법 사용할 수 있다.

2. 상대 오차 범위 사용하기

relativeEqual(a,b) = | a - b | / max( | a | , | b | )

=> 실질적인 오차 허용 범위는 a, b가 커질수록 커진다.

 

구현 코드

bool relativeEqual(double a, double b)
{
	// a와 b의 오차가 더 큰 수의 0.000001% 이하이면 true를 반환
	return fabs(a-b) <= 1e-8 * max(fabs(a), fabs(b));
}

위 방법으로 상대적인 오차 허용 범위를 구할 수 있게 됐다.

하지만 위 방법 또한 안전하지 않다. 큰 수를 비교할 때는 상관 없겠지만, 매우 작은 숫자들을 비교할 때 문제가 된다.

ex) a = 0, b = 0.00000000000001 인경우 relativeEqual(0,b) = b/b = 1

 

그래서 절대 오차와 상대 오차를 혼용해서 사용하는 다음과 같은 방법이 사용될 수 있다.

3. 절대 오차 범위, 상대 오차 범위 혼용해서 사용하기

구현 코드

// 절대 오차와 상대 오차를 모두 이용해서 두 수가 같은지 판정
bool doubleEqual(double a, double b)
{
    double diff = fabs(a-b);
    
    if(diff < 1e-10) return true; // 절대 오차 허용 범위 안에서는 절대 오차 사용
	
    return diff <= 1e-8 * max(fabs(a) + fabs(b)); // 이외에는 상대 오차 사용
}

[Reference]

[IEEE 754] floating-point에대하여 - 부동 소수점 표현에 대해 자세히 정리해 놓음

프로그래밍 대회에서 배우는 문제해결전략

728x90
반응형

'컴퓨터 기본 지식' 카테고리의 다른 글

비트 연산에 대한 이해  (0) 2020.06.24
컴퓨터의 정수 표현 방법  (0) 2020.06.24
컴퓨터의 수 표현  (0) 2020.06.24
문자 인코딩에 대한 이해  (0) 2020.06.23
스트림, 버퍼에 대한 이해  (0) 2020.06.22