[임베디드 C] 진수 & 비트 연산
진수 개념
컴퓨터는 기본적으로 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 반전
메모리 저장 구조
양수
- 100 = 0x64 = 0b
0110 0100
- 32비트이므로 0000 0000 0000 0000 0000 0000
0110 0100
- 16진수로 6 4 로 변경됩니다.
- 리틀엔디안 방식이므로
6 4 0 0 0 0 0 0
으로 저장됩니다.
음수
- 100 = 0b 0000 0000 0000 0000 0000 0000
0110 0100
- 비트 반전 0b 1111 1111 1111 1111 1111 1111
1001 1011
- +1 : 0b 1111 1111 1111 1111 1111 1111
1001 1100
= 0x F F F F F F 9 C - 리틀엔디안 방식이므로
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) |
0x12345678 | 0x78654312 |
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
: 서로 다른 자료형을 가진 변수의 값들을 서로 교환한다고 할때, 일부 숫자가 짤리게 되는 경우
Author And Source
이 문제에 관하여([임베디드 C] 진수 & 비트 연산), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@dev-hoon/임베디드-C-진수-비트-연산저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)