블로그 이미지
하루, 글. 그림. 영상매체. 표현을 도와주는 기기들. 도전 중. 동화다아아

카테고리

분류 전체보기 (176)
잡담 (1)
IT 기기-리뷰&뉴스 (7)
리뷰 - 도서 (1)
리뷰 - 영상 (0)
리뷰 - 그림/음악 (1)
내장형 하드웨어 (163)
Total630,101
Today180
Yesterday195

 컴퓨터가 실수를 표현하는 방식은 조금 복잡하며, 실수라는 것 자체가 정수보다 복잡하므로 더욱 그렇다.(실수는 정수를 포함한다.) 그렇기에 컴퓨터가 실수를 표현하는 방식을 개념적으로 생각해 보자.


  

■ 실수를 표현하는 기본 방식

실수는 다음과 같이 두가지 방식을 사용해 표현한다.

 

고정 소수점은 평소에 우리가 사용하는 방식이고,
부동 소수점은 10진수 형태로 실수를 저장할 수 없는 컴퓨터에서 2진수를 이용한 부동 소수점 방식을 통하여 실수를 저장하고 사용하게 된다.

- 실수 저장 형식은 다음과 같다.

 

float type은 4byte(32bit)의 공간에 실수를 저장한다.
부호(sign) 비트 : 1bit, 양수 0, 음수 1
지수(exponent) 비트 : 8bit, 지수에 127을 더해서 저장(-126~+127승까지)
가수(fraction, significand, manitissa) 비트 : 23bit, 가수를 저장. 이때 맨 앞의 1은 저장하지 않는다.

 

컴퓨터는 적은 비트 수를 가지고 넓은 범위의 실수를 표현하기 위해서 하나의 식을 만들었다.

 

 

 위의 그림을 보면, 변수 m과 변수 e를 지니는 식이 존재한다. 이 식이 어떻게 해서 도출된 공식인지는 지금 이해하기 쉽지 않지만. 다만 이러한 식을 이용하면 적은 비트 수를 가지고 보다 넓은 범위의 실수를 표현할 수 있다. 대략 눈짐작으로라도 m과 e에 적당한 값을 넣어보면 표현할 수 있는 값의 범위가 조금 전에 비해서 비교도 할 수 없을 만큼 넓어졌음을 바로 알 수 있다. 

 따라서 컴퓨터는 이러한 형식의 식을 미리 정의해 놓고, 메모리에 할당된 데이터의 일부 비트는 m의 값을 정하는데, 또 일부 비트는 e의 값을 정하는데 사용하게 된다.

[참고] 실제로도 4바이트 이상의 메모리 공간을 이용해서 실수를 표현한다. 여기서 2바이트만을 가지고 이야기하는 것은 표현 방식을 이해하기 위해서다.

 

■ 컴퓨터가 실수를 표현하는 방식은 오차를 지닌다.

 컴퓨터는 위와 같이 실수를 표현하며 적은 비트 수를 가지고도 넓은 범위의 실수를 표현할 수 있다는 것도 알 수 있다. 그러나 이렇게 장점만을 지니는 것은 아니다. 위의 그림과 식으로 하는 실수의 표현 방식은 넓은 범위의 실수를 표현할 수 있다는 장점과 더불어 오차가 존재한다는 단점도 지닌다.

 왜 오차가 존재하는 것인가? 그림 4-12에서 보여주는 형식의 식으로는 모든 실수를 정확히 표현할 수 없기 때문이다. 한 가지 간단한 예를 들어 보겠다. 0.0은 소수점 이상이 0이고, 소수점 미만이 0인 실수이다.

 그렇다면 위의 그림에서 제공해 주는 식의 m과 e에 적절한 값을 대입해서 0.0을 만들어 볼 수는 없다는 것을 알 수 있다. 2의 n승은 절대로 0이 될 수 없기 때문이다.

 이렇듯 컴퓨터는 우리가 표현하고자 하는 실수의 값을 정확하게 표현하는 것이 아니라, 아주 가까운, 전혀 문제가 업을 만큼의 근사치를 통해서 실수를 표현하게 된다. 따라서 실수를 표현하는데 있어서 오차가 존재하는 것은 당연한 일이며, 이러한 오차를 가리켜 부동소수점 오차라 한다. 이는 컴퓨터가 소수점 이하의 수를 표현하는데 있어서 부동소수점 방식(위의 그림에서 소개한 식)을 사용하기 때문이다.

 아래 예제는 부동소수점 오차가 발생할 수 있음을 보여주는 예제이다. 그런데 이 예제는  아직 배우지 않은 문법적 요소를 담고 있지만 간단한 구조만을 생각해 본다면 대략적인 이해가 가능할 것이다.

- 7번째 줄에서는 float형 변수 f를 선언하고 있다. 지금까지 우리는 int형 변수만을 선언해 왔다. 그러나 여기서는 float형 변수를 선언하고 있다. 이유는 실수를 저장하기 위해서다.

- 9번째 줄과 10번째 줄에서 하는 일은 변수 f에 0.1을 100번 더하는 것이다. 9번째 줄에서 선언된 문장은 for문이라는 반복문이다. 이것이 10번째 문장을 총 100번 실행하게 되는 것이다. 따라서 결과적으로 변수 f에 0.1을 100번 더하게 된다.

- 12번째 줄에서는 변수 f를 출력하고 있다. 변수 f는 실수이므로 서식 문자 %d가 아닌 서식 문자 %f를 사용해야 한다. %f는 데이터를 10진수 실수형으로 출력하라는 의미를 지ls다.

 

*실행 결과

 0.1을 100번 더했으니 10.0이 나와야 하는데 실행 결과를 보면 10.0이 아닌 다른 값이 출력되고 있음을 볼 수 있다(실행 환경에 다라 결과는 차이를 보일 수 있다). 아무리 작은 오차라 할지라도 모이면 커지기 마련이다. 출력 결과는 작은 오차가 모여서 생긴 것이다.

 

 결론을 내려 보면 다음과 같다. 컴퓨터는 실수를 100% 정확히 표현하지 못한다. 다만 근사치를 표현할 뿐이다. 따라서 실수를 표현하면 기본적으로 오차가 존재하기 마련인데, 이를 두고 부동소수점 오차라 한다.

 

[참고] 부동소수점 오차에 대한 문제는 컴퓨터의 문제이지, C언어의 문제는 아니다. 따라서 다른 프로그래밍 언어들도 기본적으로 지니고 있는 문제이다.

그렇다면 4byte, 즉 32비트 실수 연산에 대해 생각해 보자.

정수와는 달리 실수는 크게 지수부분(exponent)과 소수부분(mantissa)으로 나누어 저장되는데 가장 많이 사용하는 IEEE754 규격의 예를 생각해 보자.


 32비트의 실수가 저장되는 방식은 32 = 1(sign bit)+8(exponent)+23(mantissa) 이렇게 나누어 저장이 된다. sign bit은 양수일 때 0, 음수일 때 1이다. exponent는 2의 몇 승인지를 나타내지며 이때 bias 127방식으로 저장되기 때문에 실제 지수에 127이 더해서 저장이 된다.  mantissa부분은 소수점 아래 24bit에 해당하는데 (지수에 127을 더하는 이유는 만약 데이터가 손상되어 지수부분이 모두 0000 0000이 되었다고 생각해 보자. 지수가 0이면 결과는 1을 갖게 된다. 하지만 손상된 값은  0이 되는 것이 좋을 것이다. 만약 bias127을 사용했다면 결과는 어떤 수의 -127승이 되어 거의 0이 되는 효과를 갖게 된다.)


예를 들어 십진수 12.5 를 저장한다고 하면, 우선 이진수로는 1100.1 이렇게 될 것이다. 이것을 지수부와 소수부로 표현해 보면 1.1001*(2^3) 이렇게 될 것이며. (이때 소수점 왼쪽의 1은 hidden 1이라 하여 따로 저장되지 않는다. 당연히 1밖에 올수 없으니까.) 그럼 지수는 3이고 소수부는 1001 이 된다. bias를 적용하면 exponent=130 즉 이진수로 10000010 이 되며 mantissa는 100 1000 0000 0000 0000 0000 이렇게 23bit를 차지하게 될 것이다.


즉 실제로 저장되는 데이터는

0 1000 0010 100 1000 0000 0000 0000 0000 가 된다.



그럼 실수 45000.67 이 어떻게 저장되는지를 생각해 보자.

45000 → 1010 1111 1100 1000

0.67 → 1010 1011 1000 0101 0001 110……


0.67*2 = 1.34 →1

(1.34-1)*2 = 0.68 → 0

0.68*2 = 1.36 → 1

(1.36-1)*2 = 0.72 → 0

0.72*2 = 1.44 → 1

(1.44-1)*2 = 0.88 → 0

0.88*2 = 1.76 → 1

(1.76-1)*2 = 1.52 → 1

(1.52-1)*2 = 1.04 → 1

(1.04-1)*2 = 0.08 → 0

0.08*2 = 0.16 → 0

0.16*2 = 0.32 → 0

0.32*2 = 0.64 → 0

0.64*2 = 1.28 → 1

(1.28-1)*2 = 0.56 → 0

0.56*2 = 1.12 → 1

(1.12-1)*2 = 0.24 → 0

0.24*2 = 0.48 → 0

0.48*2 = 0.96 → 0

0.96*2 = 1.84 → 1

(1.84-1)*2 = 1.68 → 1

(1.68-1)*2 = 1.36 → 1

(1.36-1)*2 = 0.72 → 0


즉, 45000.67을 이진수로 표현하면

1010 1111 1100 1000.1010 1011 1000 0101 0001 110……


이것을 컴퓨터에서 실수를 저장하는 형식인 2진수 부동소수점 방식(1.xxx * 2^n)으로 변환하면 1010 1111 1100 1000.1010 1011 1000 0010 1110 000……는

1.0101 1111 1001 0001 0101 0111 000 * 2^15로 표현된다.


부호 1bit : 0
지수 8bit : 15 + 127 = 142 -> 1000 1110
가수 23bit : 앞에 1빼고 -> 0101 1111 1001 0001 0101 0111

따라서, 45000.67의 float type 으로 저장된 형태는 다음과 같다.
 0 1000 1110 0101 1111 1001 0001 0101 0111
 0100 0111 0010 1111 1100 1000




참고

float 부동소수점 메모리 저장 방식 (http://blog.naver.com/haha2633?Redirect=Log&logNo=60035219695)

데이터 표현 방식의 이해(http://blog.naver.com/b5m5see?Redirect=Log&logNo=90032441755)

Posted by 동화다아아

댓글을 달아 주세요

  1. 2011.04.15 16:05 CS  댓글주소  수정/삭제  댓글쓰기

    15 + 127 = 142 -> 1001 0010 이 아니라 1000 1110 아닌가요?

    0111 1111 (127)
    + 1111 (15)
    ==============
    1000 1110

  2. 2015.12.12 22:46  댓글주소  수정/삭제  댓글쓰기

    이거 중간에 그림 m이랑 e 위치 바뀐거맞죠?

최근에 달린 댓글

최근에 받은 트랙백

글 보관함