🖱️ 진수 표기법에 대하여
우리가 어릴 때 부터 지금까지 접해왔던 숫자 표기법은
10진수
이다.
하지만 프로그래밍 세계에서는 이와 별개로 3개의 표현법이 더 있다.
- 10진수는 항상 우리가 쓰던 방식이다.
- 8진수는 0~7까지는 10진수랑 같으며, 8이 되면 올림해서 10으로 표기한다.
- 2진수 컴퓨터가 받아들이는 디지털 신호이다. (0과 1로 표기)
- 16진수는 2진수를 쉽게 표현하기 위해서 4bit씩 나누어서 표기한다.
(09까지는 10진수와 같으며, 10부터는 af로 표기)
🖱️ 값과 메모리
- 어떤 값이든
2진수
로 표현할 수만 있다면
전기적 신호와 자기적 신호로 바꿀 수 있다. (★★★★★)
>RAM/HDD
에 저장할 수 있다.- 2진수로 바꾸기 위한 규칙이 필요하다. (4가지)
2의보수법을 사용한다.
- 전기적 신호와 자기적 신호가 오게 되면 바로 CPU에 가는 것이 아니다.
- CPU에는 L1, L2, L3 캐시가 있다. (아직 깊게 공부하지는 못함)
- HDD에서 CPU로 직접 명령어가 전달되지 못하며, RAM에 Loading되고 CPU의 L1캐시에 저장된다.
🖱️ RAM과 HDD에 어떻게 저장되는가
🖱️ 2진수의 값 표현 규칙(4가지)
- Sign-Magnitude(부호 절대값)부호 절대값 방식은 가장 쉽게 생각할 수 있는 방식이다.
최상위비트를 부호비트(0이면 양수, 1이면 음수)로 사용하고, 나머지는 절대값을 표현한다.
위의 예를 보면 5-3을 수행하려고 한다.
사람의 입장에서는 정말 쉽게 2라고 답변을 할 수 있지만, 안타깝게도 컴퓨터는 음수를 계산하지 못한다.
위 방법은 대한 방법으로 나온것인데 몇가지 문제점들이 보인다.- 0을 +0과 -0으로 나누기가 애매하다. - 계산한 값이 다르게 나온다.
그에 대한 대안으로 나온 것이 1의 보수법, 2의보수법이다.1의 보수는 어떤 수를 더해서 1이 되게끔 보충해주는 수를 말한다.
1의 1의 보수는 0이다.<br> 0의 1의 보수는 1이다.
결론적으로 1의보수는 2진수로 바꾼 수를 역으로 뒤집으면 된다.
하지만 아직도 값이 일치하지 못한다.2의 보수는 어떤 수를 더해서 2가 되게끔 보충해주는 수를 말한다.
2의 보수는 1의 보수에서 +1을 더한 값이다.
위의 방법으로 답을 구하면 정확하게 일치한다.
참고로 8bit를 넘어가는 수는 버린다.
Excess-K는 K로 지정된 값을 무조건 더하는 방법이다. 음수던 양수던 상관하지 않는다.
🖱️ 부동소수점 계산 방법
- 정수와 소수점를 구별한다. (12와 .375)
- 정수는 2진수로 바꾸며 소수점 아래 숫자는 다음과 같은 규칙에 의해 2진수로 변환된다.
- 소수점 이하 숫자 * 2 -> 정수 부분만 기록한다. (0,1,1)
- 소수점 이하 숫자가 0 이거나 *2를 해서 무한 반복되면 stop한다.
- 2진수 정규화를 실시한다. (1100.011 -> 1.100011 * 2^3)
- 가수부 * 2^n 으로 표시된다. (2진수이기 때문에 2의 거듭제곱으로 표현함)
- 정수부분은 버린다.
- 소수점 부분은 가수부라고 하며 2의 거듭제곱의 수를 지수부라고 한다.
- 지수부는 Excess-K 방식으로 계산하여 2진수로 바꾼다. (k = (2^n-1)-1)
- 32bit에서 지수부는 8bit이므로 k = 2^7-1 = 127
- 64bit에서 지수부는 11bit이므로 k = 2^10-1 = 1023
- 가수부는 Sign-Magnitude 방식으로 계산한다.
- 가수부를 비트 처음부터 일렬로 나열한다.
- 16진수로 나타내려면 4bit씩 나눈다.
🖱️ 비트 연산자
- 이미지 및 영상 처리에 사용된다.
- 마스킹, 오버레이 기법에 사용된다.
- 색조 변경에 사용된다.
- R(1byte), G(1byte), B(1byte) / 0~255로 세기조절
System.out.println(a & b);
// a = 0000 0000 0000 0000 0000 0000 0110 1100
// b = 0000 0000 0000 0000 0000 0000 0101 0101
// --------------------------------------------
// 0000 0000 0000 0000 0000 0000 0100 0100 = 68
System.out.println(a | b);
// a = 0000 0000 0000 0000 0000 0000 0110 1100
// b = 0000 0000 0000 0000 0000 0000 0101 0101
// --------------------------------------------
// 0000 0000 0000 0000 0000 0000 0111 1101 = 125
System.out.println(a ^ b);
// a = 0000 0000 0000 0000 0000 0000 0110 1100
// b = 0000 0000 0000 0000 0000 0000 0101 0101
// --------------------------------------------
// 0000 0000 0000 0000 0000 0000 0011 1001 = 57
// 비트 연산에서 not은 ! 연산자가 아니라 ~ 연산자 이다.
System.out.println(~a);
// a = 0000 0000 0000 0000 0000 0000 0110 1100
// --------------------------------------------
// 1111 1111 1111 1111 1111 1111 1001 0011 = -109
- 나머지 연산을 구할때도 활용된다.
System.out.println(57 % 2);
System.out.println(57 & 0b0000_0001);
System.out.println(57 & 0x1); // 맨 끝의 1비트만 비교한다. (속도가 빠르다.)
System.out.println(57 & 1);
System.out.println(57 % 4);
System.out.println(57 & 0b11);
System.out.println(57 % 8);
System.out.println(57 & 0b111); // 57 & 7
System.out.println(57 % 16);
System.out.println(57 & 0b1111); // 57 & 15 = 57 & 0xf
비트 이동 연산자 :
>>, >>>, <<
<<
연산자의 비트 이동 => 'i^이동비트'를 곱한 것- 속도가 빠르다.
int i = 1;
// [00000000000000000000000000000001] = 1
System.out.println(i << 1);
// 0[0000000000000000000000000000001 ]
// [00000000000000000000000000000010] = 2
System.out.println(i << 2);
// 00[000000000000000000000000000001 ]
// [00000000000000000000000000000100] = 4
i = 11; // [00000000000000000000000000001011]
System.out.println(i << 1); // 0[00000000000000000000000000010110] => 22
System.out.println(i << 2); // 00[00000000000000000000000000101100] => 44
System.out.println(i << 3); // 000[00000000000000000000000001011000] => 88
>>
연산자의 비트 이동 => n / 2^이동비트- 나누기 연산을 수행하는 것 보다 계산 속도가 빠르다
int i = 105; // [00000000000000000000000001101001]
System.out.println(i); // => 105
System.out.println(i >> 1);
// [ 0000000000000000000000000110100]1
// [00000000000000000000000000110100] => 52
System.out.println(i >> 2);
// [ 000000000000000000000000011010]01
// [00000000000000000000000000011010] => 26
>>>
연산자 => 왼쪽 빈자리를 음수 양수 상관없이 무조건 0으로 채운다.
int i = 105; // [00000000000000000000000001101001]
System.out.println(i); // => 105
System.out.println(i >>> 1);
// [ 0000000000000000000000000110100]1
// [00000000000000000000000000110100]1 => 52
System.out.println(i >>> 2);
// [ 000000000000000000000000011010]01
// [00000000000000000000000000011010]01 => 26
- 특정 비트 값 추출하기
- 비트연산자(>>) + 비트연산자(&)
int i = 0x27a130ff;
int a, b, c, d;
System.out.println(i);
System.out.println(Integer.toHexString(i));
// [00100111_10100001_00110000_11111111] => 27a130ff
a = i >> 24;
// [00000000_00000000_00000000_00100111]_10100001_00110000_11111111
b = i >> 16 & 0xff;
// [00100111_10100001_00110000_11111111] => 27a130ff
// [00000000_00000000_00100111_10100001]_00110000_11111111
// 00000000_00000000_00100111_10100001 => 0x000027a1
// & 00000000_00000000_00000000_11111111 => 0x000000ff
// --------------------------------------
// 00000000_00000000_00000000_10100001
System.out.println(Integer.toHexString(a));
System.out.println(Integer.toHexString(b));
- 특정 비트 값 추출하기 2
- 비트연산자(|) + 상수
final int CSS = 0x01; // 0000 0001
final int HTML = 0x02; // 0000 0010
final int PHP = 0x04; // 0000 0100
final int PYTHON = 0x08; // 0000 1000
final int JAVASCRIPT = 0x10; // 0001 0000
final int JAVA = 0x20; // 0010 0000
final int CPP = 0x40; // 0100 0000
final int C = 0x80; // 1000 0000
// C, Java, Python, HTML 을 할 줄 아는 개발자의 정보를 설정하라!
int lang = C | JAVA | PYTHON | HTML; // 10101010
// 정수 값에서 특정 비트의 값만 검사하는 방법
// 예) 10101010 (C, Java, Python, HTML)
//
// CPP 언어를 할 줄 아는지 검사하기
// 10101010
// & 01000000 (조사하려는 값과 AND 한다. 01000000)
// ----------------------
// 00000000
//
// AND 결과 값을 검사 값과 같은지 비교하면 된다.
// 00000000 (결과값)
// 01000000 (CPP 여부를 조사하는 값)
// => 결과 값과 조사한 값이 같지 않으면 해당 비트가 0이라는 의미다.
System.out.printf("CSS : %b\n", (lang & CSS) == CSS);
System.out.printf("HTML : %b\n", (lang & HTML) == HTML);
System.out.printf("PHP : %b\n", (lang & PHP) == PHP);
System.out.printf("Python : %b\n", (lang & PYTHON) == PYTHON);
System.out.printf("JavaScript : %b\n", (lang & JAVASCRIPT) == JAVASCRIPT);
System.out.printf("Java : %b\n", (lang & JAVA) == JAVA);
System.out.printf("C++ : %b\n", (lang & CPP) == CPP);
System.out.printf("C : %b\n", (lang & C) == C);
System.out.println("--------------------------");
System.out.printf("CSS : %b\n", (lang & CSS) > 0);
System.out.printf("HTML : %b\n", (lang & HTML) > 0);
System.out.printf("PHP : %b\n", (lang & PHP) > 0);
System.out.printf("Python : %b\n", (lang & PYTHON) > 0);
System.out.printf("JavaScript : %b\n", (lang & JAVASCRIPT) > 0);
System.out.printf("Java : %b\n", (lang & JAVA) > 0);
System.out.printf("C++ : %b\n", (lang & CPP) > 0);
System.out.printf("C : %b\n", (lang & C) > 0);
==============================================================================
CSS : false
HTML : true
PHP : false
Python : true
JavaScript : false
Java : true
C++ : false
C : true
--------------------------
CSS : false
HTML : true
PHP : false
Python : true
JavaScript : false
Java : true
C++ : false
C : true
🖱️ 저급 프로그래밍 언어
컴퓨터가 이해하기 쉽게 작성된 프로그래밍 언어로 일반적으로
기계어
와어셈블리어
를 일컫는다.
실행속도가 매우 빠르지만 배우기가 어려우며 유지보수가 힘든 것이 단점이다.
현재는 특수한 경우가 아니면 사용되지 않는다. -위키백과-
기계어
8B542408 83FA0077 06B80000 0000C383
FA027706 B8010000 00C353BB 01000000
C9010000 008D0419 83FA0376 078BD98B
B84AEBF1 5BC3
어셈블리어
fib:
mov edx, [esp+8]
cmp edx, 0
ja @f
mov eax, 0
ret
@@:
cmp edx, 2
ja @f
mov eax, 1
ret
@@:
push ebx
mov ebx, 1
mov ecx, 1
@@:
lea eax, [ebx+ecx]
cmp edx, 3
jbe @f
mov ebx, ecx
mov ecx, eax
dec edx
jmp @b
@@:
pop ebx
ret
🖱️ 결론
- 어떤 값이든 2진수로 표현할 수만 있다면 전기적 신호와 자기적 신호로 바꿀 수 있다.
- 내가 원하는 파일을 실행시키기 위하여 내부적으로 많은 과정들을 거친다.
- 1번과 4번의 방법은 부동소수점을 2진수로 표현할 때 지수부를 이 규칙에 따라 표현한다.
- 현대의 대부분의 컴퓨터는
2의보수
를 음수 표현 방법으로 사용한다. - 부동소수점은 지수부와 가수부로 나누어서 계산하며 32bit와 64bit에 따라 다른 결과가 나온다.
- 저급 프로그래밍 언어를 보면서 현재 나온 웹 프로그래밍의 언어에 대한 감사함이 들었다.
'IT BASE' 카테고리의 다른 글
📝 클래스 다이어그램 (Class Diagram) (0) | 2021.08.03 |
---|---|
⌨️ Compile & OS (0) | 2021.07.18 |