비트(bit)와 바이트(byte)
1bit는 2진수의 한자리 수를 의미하고, 1byte = 8bit 이다.
컴퓨터 메모리의 주소 값은 1byte당 하나의 주소가 할당되어 있다.
따라서, 1byte에는 $\sum_{i=0}^{7} 2^{i} = 2^0 + 2^1 + \text{...} + 2^7 = 2^8-1 = 255$까지의 수를 저장할 수 있다.
8진수와 16진수를 이용한 데이터 표현
//base8 & base16
#include <stdio.h>
int main(void){
// 특별한 선언이 없으면 10진수
int num1=0xA7, num2=0x43; // 0x로 시작하면 16진수
int num3=032, num4=024; // 0으로 시작하면 8진수
printf("0xA7의 10진수 정수 값: %d \n", num1); // 16*A(10) + 7 = 167
printf("0x43의 10진수 정수 값: %d \n", num2); // 16*4 + 3 = 67
printf(" 032의 10진수 정수 값: %d \n", num3); // 8*3 + 2 = 26
printf(" 024의 10진수 정수 값: %d \n", num4); // 8*2 + 4 = 20
printf("%d-%d=%d \n", num1, num2, num1-num2); // 167-67 = 100
printf("%d+%d=%d \n", num3, num4, num3+num4); // 26+20 = 46
return 0;
}
정수와 실수의 표현 방식
1byte에서 가장 왼쪽의 bit를 MSB(Most Significant Bit)라고 부르며, 부호를 나타내는 역할을 한다.
MSB를 제외한 나머지 bit는 수의 크기를 나타내는 역할을 한다.
ex) 00000101 : 5 / 10000101 : -5
음의 정수 표현 방식
- 00000101 = 5
- 보수를 취한다 (2진수에서 각 자리수를 반대로 바꿈)
- 11111010
- 1을 더한다
- 11111010 $\rightarrow$ 11111011 = -5
00000101 + 11111011 = (1)00000000 = 0 이므로 적절한 표현법
실수의 표현 방식
정수를 표현하는 방식과 유사하게 실수를 표현하게 되면, 표현할 수 있는 실수의 개수에 대한 한계가 너무 커서 제대로 표현할 수가 없다.
그렇기 때문에 실수에 대한 정확도를 조금 희생해서 표현할 수 있는 실수의 범위를 늘리는 방법을 채택하는데, 이를 floating point(부동소수점)방식이라고 한다.
컴퓨터는 이런 방식으로 실수를 표현하기 때문에, 완벽한 값으로 실수를 표현할 수 없게 된다.
// 실수 표현
#include <stdio.h>
int main(void){
int i;
float num=0.0;
for (i=0; i<100; i++)
num+=0.1;
printf("0.1을 100번 더한 결과 : %f \n", num);
// 0.1을 100번 더한 결과 : 10.000002
return 0;
}
`fixed point`와 `floating point`의 차이를 조금 더 명확하게 구분하기 위해 간단한 예시를 들어보자.
`23.625`에 대해 `fixed point`를 사용해 이진 변환을 해보면,
- 정수부
- $23_{10} = 10111_{2}$
- 소수부
- $0.625_{10} = 0.101_{2}$
- 소수부에 대한 이진 변환은 정수부와 반대로, 2를 곱해준 값의 정수부를 차례로 이어 붙이면 된다(정수부와 같은 원리)
- 정수부 : $x/2$
- 소수부 : $x/2^{-1}$
- 정수부와 소수부 연결
- $23.625_{10} = 10111.101_{2}$
다음으로, `floating point`를 사용한 이진 변환은 다음과 같다.
- `fixed point`와 동일한 방식의 이진 변환
- $23.625_{10} = 10111.101_{2}$
- Normalize(지수부 + 가수부)
- $10111.101_{2} = 1.0111101 \times 2^{4}$
- 지수부(exponent) = 4 + 127(exponent bias) = 131 $= 10000011_{2}$
- 가수부(mantissa) = $0111101_{2}$
- $10111.101_{2} = 1.0111101 \times 2^{4}$
- 지수부와 가수부 연결
- $23.625_{10} = 0(\text{sign bit})\;10000011(\text{exponent bit})\; 0111101(\text{mantissa bit})$
정리해보면, `fixed point`의 경우에는 이진 변환을 그대로 각 자리수에 채워 넣는 방식이고 `floating point`는 지수 변환을 한번 거친 후에 각 지수부, 가수부에 값을 채우는 방식이다.
따라서, `floating point`의 경우가 `fixed point`보다 표현력이 풍부하고 메모리 효율적인 것이다.
비트 연산자
비트 단위로 연산을 실행하도록 하는 연산자
연산자 | 연산자의 기능 | 결합방향 |
& | 비트 단위로 AND 연산을 한다 (num1 & num2;) |
$\rightarrow$ |
| | 비트 단위로 OR 연산을 한다 (num1 | num2;) |
$\rightarrow$ |
^ | 비트 단위로 XOR 연산을 한다 (num1 ^ num2;) |
$\rightarrow$ |
~ | 단항 연산자로서 피연산자의 모든 비트를 반전시킨다. | $\leftarrow$ |
<< | 피연산자의 비트 열을 왼쪽으로 이동시킨다. (num<<2;) - 왼쪽으로 두칸 이동 |
$\rightarrow$ |
>> | 피연산자의 비트 열을 오른쪽으로 이동시킨다. (num>>2;) - 오른쪽으로 두칸 이동 |
$\rightarrow$ |
// 비트 단위 연산자
#include <stdio.h>
int main(void){
int num1=15; // 비트 단위 : 00000000 00000000 00000000 00001111
int num2=20; // 비트 단위 : 00000000 00000000 00000000 00010100
// AND 연산자(&)
int num3 = num1 & num2; // 비트 단위 : 00000000 00000000 00000000 00000100 = 4
printf("AND의 연산 결과 : %d \n", num3);
// OR 연산자(|)
int num4 = num1|num2; //비트 단위 : 00000000 00000000 00000000 00011111 = 16+8+4+2+1 = 31
printf("OR의 연산 결과 : %d \n", num4);
// XOR 연산자(^)
int num5 = num1^num2; //비트 단위 : 00000000 00000000 00000000 00011011 = 16+8+2+1 = 27
printf("XOR의 연산 결과 : %d \n", num5);
// NOT 연산자(~)
int num6 = ~num1; /*비트 단위 : 11111111 11111111 11111111 11110000 -1 = 11111111 11111111 11111111 11101111
= 00000000 00000000 00000000 00010000 = 16의 보수 => -16*/
printf("NOT 연산의 결과 : %d \n", num6);
// Shift 연산자(left : <<)
int num7 = num1<<1; // 비트 단위 : 00000000 00000000 00000000 00001111 -> 00000000 00000000 00000000 00011110 = 2+4+8+16 = 30
int num8 = num1<<2; // 비트 단위 : 00000000 00000000 00000000 00001111 -> 00000000 00000000 00000000 00111100 = 4+8+16+32 = 60
printf("1칸 왼쪽 이동 결과 : %d \n", num7);
printf("2칸 왼쪽 이동 결과 : %d \n", num8);
// Shift 연산자(right : >>)
/* CPU에 따라서 연산 결과가 달라짐
ex) -16>>2
- 비는 자리에 0이 채워지는 경우 : -16 = 11111111 11111111 11111111 11110000 -> 0011111111 11111111 11111111 11111100 (양수)
- 비는 자리아 1이 채워지는 경우 : -16 = 11111111 11111111 11111111 11110000 -> 1111111111 11111111 11111111 11111100 (음수)
*/
int num = -16;
int num9 = num>>2; // -16 = 11111111 11111111 11111111 11110000 -> ??111111 11111111 11111111 1111100 = -4 => 1로 채워짐
int num10 = num>>3; // -16 = 11111111 11111111 11111111 11110000 -> ???11111 11111111 11111111 1111110 = -2 => 1로 채워짐
printf("2칸 오른쪽 이동 결과 : %d \n", num9);
printf("3칸 오른쪽 이동 결과 : %d \n", num10);
return 0;
}
'공부 > C언어' 카테고리의 다른 글
C언어 - (6) : 반복문 (0) | 2024.10.07 |
---|---|
C언어 - (5) : printf / scanf (3) | 2024.10.06 |
C언어 - (4) : 자료형/형 변환 (3) | 2024.10.03 |
C언어 - (2) : 변수 / 연산자 (0) | 2024.09.09 |
C언어 - (1) (0) | 2024.09.05 |