[임베디드 C] 진수 & 비트 연산

정재훈·2022년 3월 31일
0

임베디드 C언어

목록 보기
3/11
post-thumbnail

진수 개념

컴퓨터는 기본적으로 2진수를 사용하고, 사람은 10진수를 사용합니다. 하지만, 2진수에서 10진수로 변환하는데 계산이 오래걸리고, 2진수를 보다 알아보기 쉽게 하기 위해 16진수를 사용합니다.

2진수 표기

[숫자0] + [b] + [2진수] : b는 binary를 뜻합니다. 대소문자 구분하지 않습니다.
ex)0b111100 => 0011 1100

8진수 oct , 10진수 dec , 10진수는 앞에 0D를 생략한다.

16진수 표기

[숫자0] + [x] + [16진수] : x는 hex를 뜻합니다. 대소문자 구분하지 않습니다. x로 쓰나 X로 쓰나 상관없다.
ex) 0x4EFF10

2진수와 16진수를 변환하는 방법

뒤에서 부터 4자리씩 끊어서 계산하면 됩니다.

진법을 변환하는 함수

strtol (16 or 2 -> 10)

strtol : stdlib.h를 통해 사용 , 10진법으로 변환하기

#include <stdio.h>
#include <stdlib.h>

int main()
{
	char input[10] = "38A5";
	
    // 16진수 -> 10진수
	int result = strtol(input,NULL,16); // 14501
	
	return 0;
}
#include <stdio.h>
#include <stdlib.h>

int main()
{
	char input[10] = "1001011";
	// 2진수 -> 10진수
	int result = strtol(input,NULL,2); // 75

	return 0;
}

sprintf (10 -> 16)

#include <stdio.h>
#include <stdlib.h>

int main()
{
	int a = 100; 
	char buf[10];

	sprintf(buf, "%X", a); // buf = 64

	int de = 1;

	return 0;
}

10진법 -> 2진법 : 직접구현

#include <stdio.h>
#include <string.h>

int main()
{
	int a = 17;
	char buf[10] = {0};
	int bn = 0;

	for(int i = 17; i != 0; i /= 2)
	{
		// 나머지가 0일시 '0' 삽입 , 나머지 1일시 '1' 삽입
		buf[bn++] = (i % 2) + '0'; 
	}

	for(int i = strlen(buf) -1; i >= 0; i--)
	{
		printf("%c", buf[i]);  1 0001
	}


	return 0;
}

비트연산 기본

비트 연산이 필요한 이유

MCU(Micro Controller Unit) : 하나의 칩 안에 CPU / Memory / Disk를 포함한 장치
해당 장치를 개별적으로 작동시키기 위해서 비트 연산이 사용됩니다.
PortA = PortA & ~(0x1<<2)

비트 연산자

&연산 (and)

값을 추출할 때 사용, 두 값 모두 1이 적힌 곳만 추출한다.

|연산 (or)

2진수 덧셈시 사용되고, 둘 중 한 곳에 1이 있으면 추출한다.

^연산 (xor)

두 값이 같으면 0 다르면 1 을 추출합니다. 가산기(덧셈기)라고 외우면 편합니다.

~연산 (not)

0을 1로 1를 0으로 바꿔주는 연산자입니다.

<< Left Shift

값을 왼쪽으로 밀어서 숫자를 추가해준다.
ex) 110111 << 2 => 11011100 : 밀어서 발생한 자리는 0으로 채웁니다.

데이터형이 특정비트로 정해져있다면 에러가 발생한다.
ex) 1100 0010 <<2 : 11 0000 1000 하면 11이 날라가버립니다.

또한, Left shift는 unsigend형에 많이 사용됩니다. signed는 부호가 있는 자료형으로 맨 앞 비트를 부호비트로 사용하여, 날라갈 수도 있기 때문에, unsigned에서 사용됩니다.

>> Right Shift

값을 오른쪽으로 미는데, 숫자가 없어진다.
ex) 110111 >> 2 => 1101 : 2번 밀어 11이 없어졌다.

비트 단위

bit : 데이터 저장을 위한 최소 단위 ( 0 or 1)
(nibble) : 4bit
byte : 8bit
KB = 1024B
MB = 1024KB
word : cpu가 한번에 처리할 수 있는 데이터 버스 폭(가변) , 컴퓨터 CPU는 보통 32bit/64bit

문제) cpu 8bit이고 int형 32bit라면?
32bit를 8bit로 4번 쪼개서 실행하므로, 처리 속도가 느려집니다.

LSB / MSB

LSB : Least Significant Bit , 최하위 비트
MSB : Most Significant Bit , 최상위 비트

Char & Unsigned char

char : MSB를 부호로 사용하고 나머지 7bit에 수를 저장하는 용도로 범위는 -128 ~ 127
unsigned char : 부호는 없고, 8bit 모두 수를 저장하는 용도로 범위는 0 ~ 255

비트 추출하기

[변수명] = (추출 대상 >> 추출 bit) & 0x01 : 추출대상의 추출 bit를 추출하여 변수명에 저장하기 , 여러 bit 추출하고자 하면 0x01 값을 조정해줍니다.
ex)b = (a>>2) & 0x01 : 2번 bit를 추출하여 변수 b에 저장하기

clear & set

기존비트 & 0 = 0
기존비트 & 1 = 기존비트
기존비트 | 0 = 기존비트
기존비트 | 1 = 1

의 개념을 잘 이해하고 다음 코드를 이해해봅시다.

비트 Clear

비트 clear : 특정 비트를 0으로 만든 것
[변수명] = [현재바이트] & ~(1 << n); : 현재바이트의 n번째 bit를 clear(0)으로 만들어 변수에 담아주는 함수, 여러 bit 변경하고자 하면 1의 값을 조정해줍니다.

비트 Set

비트 set : 특정 비트를 1로 만드는 것
[변수명] = [현재바이트] | (1 << n) ; : 현재바이트의 n번째 bit를 set(1)으로 만들어 변수에 담아주는 함수, 여러 bit 변경하고자 하면 1의 값을 조정해줍니다.

비트반전 (XOR 이용)

^연산으로 두 값이 같으면 0 다르면 1 을 추출합니다. 가산기(덧셈기)라고 외우면 편합니다.
xor 특성상 1과 xor이 되어야 해당 비트가 반전됩니다.
ex) 0000 ^ 0001 = 0001 , 0101 ^ 0001 = 0100
[변수명] = [현재바이트]^(1<<n) : 현재바이트의 n번째 bit를 반전 시켜 변수명에 담기 위한 함수 , 여러 bit 변경하고자 하면 1의 값을 조정해줍니다.

ex) b2xor = a^(1<<2); : xor을 이용하여 2번 bit 반전

메모리 저장 구조

양수

  1. 100 = 0x64 = 0b 0110 0100
  2. 32비트이므로 0000 0000 0000 0000 0000 0000 0110 0100
  3. 16진수로 6 4 로 변경됩니다.
  4. 리틀엔디안 방식이므로 6 4 0 0 0 0 0 0 으로 저장됩니다.

음수

  1. 100 = 0b 0000 0000 0000 0000 0000 0000 0110 0100
  2. 비트 반전 0b 1111 1111 1111 1111 1111 1111 1001 1011
  3. +1 : 0b 1111 1111 1111 1111 1111 11111001 1100 = 0x F F F F F F 9 C
  4. 리틀엔디안 방식이므로 9 C F F F F F F로 저장됩니다.

2의 보수

쉬운방법 : 숫자를 뒤집고, 1을 더한 수
어려운 방법 :
보수란 보충 해주는 수를 의미한다.
K^(N+1) = M + C : N비트에서 M의 (K의) 2의 보수는 C이다. 보통 N는 32비트이다.

2의 보수를 두 번 가해지면 기존 자기 자신이 나온다.

2의 보수 장점

뺄셈기 없이 덧셈기로만 뺄셈을 구현할 수 있다. -> 경제적이다.

엔디안

CPU가 메모리에 값을 저장할 때의 저장 순서
바이트 단위로, 어떤 순서로 기록할 것인지에 따라 빅엔디안리틀엔디안으로 나뉩니다.
CPU 제조사에 따라 저장방식이 다르다.
통신으로 타 CPU 내장 장치에 메모리를 전송하게 되면, 엔디안의 방식을 유의해줘야합니다.

리틀엔디안 : 0x12345678 -> 0x87654321 이 아닌 Byte 단위로 뒤바뀌는 것이기 때문에, 0x78654312임을 유의해야합니다.

빅엔디안리틀엔디안
사람이 읽기 편한 방식으로 저장(디버깅이 편함)임베디드 장치가 선택한 방식(Intel/AMD CPU, ARM)
0x123456780x78654312

Overflow / Underflow

char & unsigend char

#include <stdio.h>

int main()
{
	char a = -7;   // a = 1111 1001  => -7
    unsigned char b = a; // b = 1111 1001  => 249
    
	retunr 0;
}

Type에 따라 같은 메모리 값인데 실제 값은 달라질 수 있다.

char의 저장 범위 : -128 ~ 127 으로 저장 범위보다 큰수(128)가 저장되면 overflow, 저장범위보다 작은 수(-129)가 저장되면 underflow라고 한다.

overflow & underflow 발생 시

다음 원과 같이 실행되는데, 예를들어 -128에서 1을 빼면, -129가 되는게 아닌 127이 된다.

overflow와 underflow는 값이 깨지는 것 처럼 큰 에러가 발생되는게 아닌 메모리를 저장하는 하나의 체계라고 생각하자!

Truncation

Truncation : 서로 다른 자료형을 가진 변수의 값들을 서로 교환한다고 할때, 일부 숫자가 짤리게 되는 경우

profile
여러 방향으로 접근하는 개발자

0개의 댓글