C언어에서 형의 변환 - 1

CHOI·2021년 6월 16일
0

C 언어

목록 보기
11/28
post-thumbnail

모두 그동안 배워서 알고 있듯이 C언어에는 변수마다 형(type)이 있다.

int a; 를 한다면 a는 int 형, double b; 를 한다면 b는 double 형이다.

그런데 가끔 프로그래밍을 하다보면 형이 다른 변수끼리 대입하는 연산이 필요한 경우가 생긴다. 예를 들어서 doouble 형 변수를 int 형 변수에 넣는다거나 int 형 변수를 char 형 변수안에 넣는다거나

하지만 이처럼 형이 다른 변수끼리 대입을 하는 것은 불법이다. 마치 미국에 가서 한국돈으로 물건을 구매 하는 것과 같은 것이다.

예제

#include <stdio.h>
int main() {
  int a;
  double b;

  b = 2.4;
  a = b;

  printf("%d\n", a);
  return 0;
}

그런데 막상 해보면 문제 없이 출력이된다. 그러나

이런 오류가 발생한다. 대충 해석해보자면 'double' 로 부터 'int' 로의 형 변환, 데이터의 손실이 예상됨. 이라는 뜻이다. 이처럼 컴퓨터는 우리가 형이 다른 변수끼리 대입시 데이터가 손실될 수 있다고 알려준다.

실제로 결과값을 보면 데이터 손실이 일어나 2.4가 2로 출력이 되었다.(만약 %d 때문에 정수만 표현되는거 아닌가 라고 생각할 수 도 있지만 %f 를 해봐도 오류가 발생하는 것을 알 수 있다.)
int 형 변수는 정수부분만 받아들일수 있기 때문에 double 형 변수에서 정수부분만 들어가게 되는 것이다. 이는 각 변수가 메모리상에 저장되는 특징이 다르기 때문이다. 왜냐면 int 형의 경우 정의 했을 때 부터 메모리상에 오직 정수부분만 받아들이도록 설계되었기 때문이다.

그러면 여기서 또 의문점이 생긴다.

그렇다면 컴퓨터는 실수형을 어떻게 받아들이는 거야?

컴퓨터가 실수를 표현하는 원리

모두 알고 있듯이 컴퓨터는 이진수로 모든 데이터를 표시한다. 우리는 이전에 컴퓨터가 어떻게 이진수를 통해서 양의 정수를 표현했고 어떻게 음의 정수를 표현했는지 알아보았었다.

이번에는 컴퓨터가 어떻게 이진수로 실수를 표현하는지 살펴보자.

컴퓨터가 실수를 표현하는 방식은 크게 두가지이다.

  1. 고정 소수점(Fixed Point) 방식
  2. 부동 소수점(Floating Point) 방식

우리가 지금까지 실수를 float 라고 한 것에서 알 수 있듯이 우리는 대부분 부동 소수점 방식을 사용하는 컴퓨터를 사용한다. 그 이유는 같은 비트를 가지고 고정 소수점에 비해 부동 소수점 방식이 더 넓은 수의 범위를 표현할 수 있기 때문이다.

이렇게 부동 소수점을 통해서 실수를 표현하는 방식은 국제전자전기기술자협회(IEEE)에서 IEEE-754라는 이름으로 표준화 하였다.

우리는 123 / 123,1234 / -234 를 다음과 같이 표현할 수 있다.


이는 과학적 표기 라고 부른다. 이와 마찬가지로 컴퓨터도 다음과 같이 소수를 표현합니다.

±f×be±f×b^e

이때 f가수, b, e지수이다.

예를 들어서 123 의 경우 1.23가 f, 10이 b, 2는 e 이다.

컴퓨터는 이진체계를 사용하기 때문에 b 는 2로 고정되어 있다. 따라서 소수 데이터를 보관할 때 fe 값만 저장하게 된다. 그리고 맨 앞에 부호를 표현하기 위해 1비트를 더 쓰게 된다.

부호비트가 0이면 양수, 1이면 음수가 된다.

우리가 자주 사용하는 float 는 가수 부분이 23비트를 차지하고, 지수비트가 8비트, 부호비트가 1비트를 차지하여 총 32비트 즉 4바이트를 사용한다.

반면에 double 의 경우 가수 부분이 52비트, 지수비트가 11비트, 부호비트가 1비트로 무려 8바이트를 사용한다.

2진법 소수를 10진법으로 바꾸기

그렇다면 먼저 10010.1011(2)10010.1011_{(2)}​
를 10진법으로 바꿔보자 소수점이 내려갈 때 마다 21,222^{−1},2^{−2} ... 이런식으로 내려가는 건 10진법이랑 똑같다. 따라서

10010.1011(2)=24+21+21+23+24=18+0.5+0.125+0.0625=18.687510010.1011_{(2)}=2^4+2^1+2^{−1}+2^{−3}+2^{−4}=18+0.5+0.125+0.0625=18.6875

이와 같이 된다. 2진법으로 표시된 소수들은 모두 10진법으로 그렇다면 10진법으로 표현된 소수도 2진법으로 변환할 수 있을까? 이번에는 18.6875를 2진법으로 바꿔보자

118.625=1110110(2)0.625=1110110(2)2123=1110110.101(2)−118.625=−1110110_{(2)}−0.625=−1110110_{(2)}−2^{−1}−2^{−3}=−1110110.101_{(2)}

이렇게 바꿀 수 있다. 그러나 모든 10진법의 소수들을 2진법으로 바꾸는 것은 불가능하다.

예를 들어서 0.1을 2진법으로 바꾸어보자

0.1=24+25+28+29+=0.0001100110011...(2)0.1=2^{−4}+2^{−5}+2^{−8}+2^{−9}+⋯=0.0001100110011..._{(2)}

이와 같이 무한 소수가 나타난다 컴퓨터는 이렇게 무한대로 나타낸 수를 저장할 수 없기 때문에 일정부분만 잘라서 처리하게 되고 그 과정에서 어쩔수 없이 오차가 발생하게 된다.

IEEE 754 방식으로 이진수 소수 저장하기

자 이제 IEEE 754 방식 하에 소수가 어떻게 저장되는지 알아보자

가장 먼저 부호비트에 0이상이면 0을 아니면 1이 할당된다. 앞서 -118.625같은 경우 부호비트에 1이 할당된다.

두 번째로 변환된 이진수를 정규화(Normalization)한다. 정규화란 어떠한 이진수를 1.xxxx 꼴로 바꿔주는 것이다. -118.625 같은 경우 이진수로 1110110.101(2)−1110110.101_{(2)}이니까 1110110.1011.110110101 로 바꾸는 것이다. 그렇다면 가수부분에 xxxx 부분 즉 110110101 이 저장된다.

이때 정규화 작업에 쉬프트 연산이 얼마나 나왔는지를 계산하여 지수부분에 얼마가 와야하는지 알게 된다.
1110110.1011.110110101 로 바꾸는데 쉬프트 연산이 오른쪽으로 6번 일어난 것이기 때문에 지수에는 6이 오게 된다.

0.1 과 같은 무한 소수로 표현되는 경우에는 반올림을 하게 된다. 예를 들어서 0.1 = 0.00011001 10011001 10011001 10011001 10011001...로 나가게 되는데 float 에 대입을 한다면 float 의 가수 부분이 23비트 이므로 24비트에서 반올림을 하게 된다. 따라서 0.1 은 컴퓨터 상에 0.00011001100110011001101 로 보관하게 된다

마지막으로 계산된 지수에 바이어스(Bias) 처리를 하게 된다. 이는 그냥 지수에 2e112^{e−1}−1 만큼 더해준다는 뜻이다. 이 때 e 의 값은 지수 부분의 비트 수로, float 이면 8 이므로 127을, double 이면 11 이므로 1023을 더한다.

이렇게 바이어스 처리를 해주는 이유는 지수가 음수일 수 도 있기 때문이다. 예를 들어서 0.625 는 이진수로 0.101 인데 이러면 왼쪽으로 쉬프트를 한번 즉 지수는 -1 인 음수가 된다.

앞서 2의 보수 표현법을 배운 우리의 입장에서는 정수를 2의보수로 표현하듯이 지수도 2의 보수 표현법으로 나타내면 안된다고 할 수 있지만 무조건 양수로 값을 집어 넣는 것이 컴퓨터의 입장에서 처리하기 더 쉽기 때문이다.

아무튼 float 의 경우 지수에 들어가는 값의 범위는 1부터 254까지이고 double 의 경우 지수에 들어가는 값의 범위는 1부터 2046까지 가능하다. 이말은 float 의 지수부분이 21262^{-126} 부터 21272^{127}까지 가능하다는 의미이다.

자 그렇다면 -118.625 의 경우 지수부분에 6 + (127) = 133 이 들어가게 된다. 133은 2진수로 10000101 이다. 따라서 float a = -118.625; 를 한 변수 a 의 메모리 구조를 보면 다음과 같다.

그런데 왜 float 의 경우 지수에 들어가는 범위가 0부터 255까지가 아니고 1부터 254까지일까? 이 의문점에 대한 답변은 다음 포스팅에서..

profile
벨로그보단 티스토리를 사용합니다! https://flight-developer-stroy.tistory.com/

0개의 댓글