(Day7) - 컴퓨터 과학 기초 (이진수로 표현하기)
이 글은 제가 교육을 수강하며 기록하고 추가한 내용입니다. 강사님과 무관하게 잘못된 내용이 있을 수 있습니다.
클라우드 기반 웹 데브옵스 프로젝트 개발자 교육 과정 (5기)
- 비트캠프 엄진영 강사님 (https://github.com/eomjinyoung/)
- 훈련기관 : 네이버클라우드주식회사
- 기간: 2023-11-14 ~ 2024-5-22
- 남은 일자 : 122 일 ( 7/129 )
7일(2023-11-22,수)
강의 내용
Gradle 자바 프로젝트를 Eclipse IDE로 임포트 하는 방법
먼저 빌드 스크립트 파일 build.gradle
을 설정해줘야 한다. 이클립스와 관련 있는 부분만 보면 아래와 같다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
plugins {
id 'application'
id 'eclipse' // 이클립스 플러그인
}
... 생략 ...
// eclipse 프로젝트 이름을 설정하기(Eclipse로 컴파일 할 때)
eclipse {
project {
name = "myapp"
}
jdt {
sourceCompatibility = 17
targetCompatibility = 17
javaRuntimeName = "JavaSE-17"
}
}
... 생략 ...
IntelliJ로 gradle 프로젝트 임포트하기?
jetbrains 에서 아주 쉽게 잘 만들어줬다. settings.gradle
파일을 임포트하면 전체 프로젝트를 임포트할 것이냐고 물어본다.
프로그래밍 기초
정수를 표현하는 방식: 10진수, 8진수, 16진수, 2진수
수는 수다. 몇 개가 있는지 하는 그 수. 근데 숫자는 다르다. 숫자는 기호다.
진법은 숫자 기호를 무한히 많이 만들 수는 없으니, 몇개의 묶음을 자릿수를 바꿔서 표현하자는 것이다.
컴퓨터에서 데이터를 저장하는 원리: RAM, HDD(마그네틱)
흔히들 이진수가 컴퓨터에 저장된다고 표현하는데, 정확하게 표현하면 전자기적 신호의 있고 없음이 저장되는 것이다. 그것도 엄청나게 많이. 이러한 신호의 있고 없음을 표현하기 좋은 진법이 이진법이다. 있으면 1, 없으면 0으로 표현하면 되니까.
숫자, 문자, 색상 등의 데이터를 메모리에 저장하는 방법
세상을 설명하기 위한 수
는 자연수, 정수, 유리수, 실수, 무리수, 허수까지 다양하지만 컴퓨터는 digital(셀 수 있는 불연속적인 신호)
들만을 다룰 수 있는 기계이다. 숫자, 문자, 색상들도 이러한 불연속적인 신호들로 저장하고 다뤄야 한다. 모든 데이터에 대해 그렇다.
문자는 이진수에 문자를 대응시켜 저장한다. (문자 집합) 색상은 빛의 3원색인 Red, Green, Blue 각각의 값의 크기로 저장한다. 보통은 각 색을 8비트로 저장한다. 그럼 각 원색의 세기를 2^8^ = 256가지 경우의 수로 표현할 수 있다. 이 경우 표현할 수 있는 색은 (2^8^)^3^= 2^24^= 16,777,216 가지 수 이다. 사람들이 여기에 이름을 붙였다. 그것이 트루컬러
이다.
숫자는 정수와 부동소수점을 다룬다. 자세한 내용은 아래에.
정수를 2진수로 변환하는 4가지 방법: Sign-Magnitude, 1’s Complement, 2’s Complement, Excess-K
사람들이 기계를 만들면서 신호의 있고 없음들로 숫자를 처리하려고 하면서, 어떻게 수를 기계에 저장해야 할 지
고민하기 시작했다. 가장 간단한 방법부터 시작해서, 지금 현대 컴퓨터가 사용하고 있는 방법까지 고안되었다.
우선 정수에 대해서 알아보자.
가장 간단한 방법. 맨 앞의 비트는 부호를 표현한다. 0이면 양수, 1이면 음수
이런식이다. 뒤 비트들은 크기를 표현한다. 그래서 부호-크기 방법이라고 해서 Sign-Magnitude 이라는 방법이 만들어졌다. 근데 기계의 관점에서 보면 이 방법은 문제가 많다.
- +0, -0 두 0이 생긴다. (1 000) (0 000)
- 연산이 복잡해진다.
연산 복잡성 문제를 해결하려고 1의 보수법
이라는 걸 고안한다. 음수는 양수 비트를 뒤집어서 표현하자는 게 1의 보수법이다.
+3 = 0011 -3 = 1100
이 비트들을 자리수마다 더한다고 생각하면? 1111 이 나온다. 1111은 -0 이다. +0, -0 문제는 아직 남아있다.
+2 = 0010 -4 = 0100 뒤집기 = 1011 둘이 더하면? 1101 1101을 뒤집으면? 0010 그러므로 1101=-2 로 계산은 아주 간단해진다. 기계로 구현하기가 더 쉬워진다! 그러나.. 부호-크기법에 비해 자리 올림 처리는 복잡해진다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
"보수"는 어떤 수에 대한 보완적인 값이나 부호를 의미합니다. 주로 컴퓨터 과학에서는 음수를 나타내는 방법으로 사용되며, 주로 1의 보수와 2의 보수로 구분됩니다.
1. **1의 보수 (One's Complement):**
- 1의 보수는 각 비트를 반전시키는 방법입니다. 0은 1로, 1은 0으로 바꾸는 것입니다. 이 때문에 1의 보수에서는 양수와 음수를 나타내는데에 비트 패턴이 중복되어 나타납니다. 자리 올림이 발생하는 경우 처리가 복잡해지는 단점이 있습니다.
- 예시: 4비트에서 `+3`은 `0011`, `-3`은 `1100`으로 표현됩니다.
2. **2의 보수 (Two's Complement):**
- 2의 보수는 1의 보수에서 1을 더한 것입니다. 각 비트를 반전시킨 후 1을 더해줍니다. 2의 보수는 양수와 음수의 표현이 한 가지 방식으로 구분되어 있으며, 덧셈과 뺄셈에서 자리 올림이 효과적으로 처리됩니다.
- 예시: 4비트에서 `+3`은 `0011`, `-3`은 `1101`으로 표현됩니다.
3. **Excess-K (과잉-K) 표현:**
- Excess-K는 어떤 상수 K를 더해 음수를 표현하는 방식입니다. 이는 부동 소수점 표현이나 문자 코드에서 사용되기도 합니다.
4. **부호-크기 (Sign-Magnitude):**
- 부호-크기 표현에서는 가장 왼쪽 비트가 부호를 나타내며, 나머지 비트는 숫자의 크기를 나타냅니다. 0과 -0이 서로 다른 비트 패턴으로 나타나게 됩니다.
이러한 보수의 개념은 주로 음수를 나타내는데 사용되며, 특히 컴퓨터에서 정수 표현에 적용되어 있습니다.
2의 보수법은 다 뒤집은 다음 1을 더하는 것이다. 이는 가장 오른쪽에 있는 1
의 왼쪽 비트들을 뒤집는 것과 같다. 이렇게 되면 많은 문제가 해결된다. +0, -0 문제가 해결된다. 4비트라면 0000 만이 0이다. 1111은 1을 빼고 뒤집으면 되니, 1110->0001 = -1이다. 0000 = 0 (다 뒤집고 1 더해도 0000이 된다!) 0001 = 1 0010 = 2 0011 = 3 0100 = 4 0101 = 5 0110 = 6 0111 = 7 1000 = 8 또는 -8 둘 다 가능한데 -8로 약속한다. (연산 구현이 더 편하기 때문) 1001 = -7 1010 = -6 … 1111 = -1
모든 건 문제를 해결하며 발전한다.
부동소수점을 2진수로 변환하는 방법: IEEE-754 명세
IEEE의 박사들이 머리를 싸매고 만든 표준이다. 정수가 아닌 경우를 어떻게 불연속적인 신호의 연속으로 표현할 수 있을까?
메모리의 입출력 기본 단위
바이트다. 1비트는 너무 작다~ 메모리 컨트롤러 생각도 좀 해줘라.
자바에서 정수와 부동소수점을 저장하는 메모리 크기와 값의 범위
정수 데이터 타입
데이터 타입 | 크기 (바이트) | 최소값 | 최대값 |
---|---|---|---|
byte | 1 | -128 | 127 |
short | 2 | -32,768 | 32,767 |
int | 4 | -2^31 | 2^31-1 |
long | 8 | -2^63 | 2^63-1 |
부동소수점 데이터 타입
데이터 타입 | 크기 (바이트) | 유효 자릿수 |
---|---|---|
float | 4 | 약 7자리 |
double | 8 | 약 15자리 |
부동소수점의 반올림 오차
반올림 오차는 부동소수점 연산 중에서 실제 값과 다른 매우 작은 오차가 발생하는 현상입니다. 이를 자바 코드를 사용하여 설명해보겠습니다. 예를 들어, 아래의 코드에서는 0.1과 0.2를 더한 후에 0.3과 비교하는 간단한 예제를 제시하겠습니다:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class RoundingErrorExample {
public static void main(String[] args) {
double result = 0.1 + 0.2;
double expected = 0.3;
// 부동소수점 연산 결과와 기대값을 출력
System.out.println("Result: " + result);
System.out.println("Expected: " + expected);
// 부동소수점 연산 결과와 기대값이 거의 같은지 비교
if (result == expected) {
System.out.println("They are equal.");
} else {
System.out.println("They are not equal.");
}
}
}
이 코드를 실행하면, 부동소수점 연산 결과와 기대값이 거의 같지 않음을 확인할 수 있습니다. 이는 부동소수점 연산의 한계로 인해 발생하는 반올림 오차의 예시입니다.
부동소수점 값을 비교할 때는 절대적인 동등성 비교(==
)를 피하고, 대신에 오차 범위 내에서 비교하는 방식을 고려하는 것이 좋습니다. 아래는 수정된 코드의 예시입니다:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class RoundingErrorExample {
public static void main(String[] args) {
double result = 0.1 + 0.2;
double expected = 0.3;
// 부동소수점 연산 결과와 기대값을 출력
System.out.println("Result: " + result);
System.out.println("Expected: " + expected);
// 부동소수점 연산 결과와 기대값이 거의 같은지 비교
double epsilon = 1e-10; // 허용 가능한 오차 범위
if (Math.abs(result - expected) < epsilon) {
System.out.println("They are equal within the tolerance.");
} else {
System.out.println("They are not equal.");
}
}
}
위 코드에서 epsilon
은 허용 가능한 오차 범위를 나타내며, Math.abs(result - expected)
는 두 값 간의 차이를 계산합니다. 이렇게 비교하면 부동소수점 연산의 오차를 고려하여 두 값이 거의 같은지 판단할 수 있습니다.
학습 점검 목록
Gradle 자바 프로젝트를 IntelliJ IDE로 가져오는 방법을 아는가?
settings.gradle
파일을 IntelliJ 에서 열면 프로젝트 임포트가 가능하다.
정수를 2진수로 바꾸는 4가지 방법을 설명할 수 있는가?
이 질문을 풀어서 말하면 있고 없음을 표현하는 비트를 여러개 가지고 있을 때, 이걸로 정수를 표현하는 방법들을 말해보라는 것이다.
- 부호-크기 : 앞 1비트 부호, 나머지 비트는 크기. +0, -0 문제와 연산 복잡성 문제.
- 1의 보수법 : 음수는 양수 비트를 뒤집는다
(0이면 1으로, 1이면 0으로)
. 연산이 간단해진다. +0, -0 문제 그대로. 자리 올림수 처리 따로 해줘야 하는 문제. - 2의 보수법 : 음수는 양수 비트를 뒤집고 1 더한 것으로 한다.
- 이러면 전부 0 일때만 0을 표현하게 된다.
- 연산이 간단해진다.
- 자리 올림수 처리 문제도 편하게 해결된다.
- Excess-K : K를 기준으로 0을 설정하자는 것이다. Excess-8(십진수) 라고 하면 8을 0으로 해서 표현하자는 것. 그러면 표현이 간단해진다. 1000 = 0 1001 = 1 1010 = 2 … 1111 = 7 반대로 8보다 작아지면 음수를 표현하게 된다. 표현이 아주 간단하지만 연산이 불편하다. 그래서 정수를 표현할 때는 안 쓰고, 부동소수점에서 쓴다.
부동소수점을 2진수로 바꾸는 방법을 설명할 수 있는가?
부동소수점은 모두 IEEE 754 표준을 따른다. 32비트, 혹은 64비트의 부동소수점에서 각 비트들이 무엇을 의미하는지는 IEEE 754 에 따라 약속이 되어있다는 말이다.
8바이트 배정밀도 부동소수점보다는 4바이트의 단정밀도 부동소수점이 다루기 쉬우니 그걸로 한번 예시를 들어보자.
단정밀도 부동소수점은 32개의 신호의 있고 없음이 나열된 것이다. 즉 32비트의 정보다. 01000000101000000000000000000000
IEEE는 심사숙고한 끝에 아래를 표준으로 정했다.
- 1 번째 비트를 부호를 나타내기로 했다. (비트 1개)
- 2~9 번째 비트는 지수를 나타내기로 했다. (비트 8개)
- 10~32 번째 비트는 가수를 나타내기로 했다. (비트 23개)
왜 그런가? 정수에서 봤던 것처럼, 연산의 효율성부터 자주 사용되는 수의 범위 등 몇날 몇일을 고민해보니 이게 적당하다고 IEEE 내부의 집단지성이 판단한 것이다. 자세한 방법은 넘 잘 설명한 글이 있어서 소개한다.
https://woo-dev.tistory.com/92 프로그래밍에서 많이 사용되는 부동 소수점이 표현되는 방식에 대해 알아보겠습니다.
우선 부동 소수점은 부호부, 지수부, 가수부로 나뉩니다.
32비트
부호부: 최상위 1비트
지수부: 8비트
가수부: 23비트
출처: 위키피디아
-118.625라는 수를 부동소수점 방식으로 변환해보겠습니다.
- 먼저 음수이므로 최상위 비트를 1로 설정합니다. (양수일 경우 0)
여기선 가독성을 위해 4bit 마다 공백을 표시하겠습니다.
절댓값 118.625를 이진법으로 변환합니다. (소수점 이진법으로 표기하는 법)
118.625 -> 1110 110.101소수점을 왼쪽으로 이동시켜 정수부가 한자리가 되도록 합니다.
1110 110.101 -> 1.1101 1010 13번에서 이동시킨 자릿수(6)만큼을 2의 지수로 사용하여 곱해주고, 이 수를 정규화된 부동 소수점이라고 합니다.
1. 1101 1010 1 x 2^64번의 소수점 아래 부분(1101 1010 1)이 가수부(23bit)가 되도록 나머지 비트를 0으로 채웁니다.
1101 1010 1000 0000 0000 000 x 2^6위 23비트를 가수부로 설정합니다.
32bit IEEE 754 형식엔 “Bias” 라는 고정된 값이 있습니다. 이는 127이며, bias를 2의 지수인 6에 더하고 이진수로 변환합니다.
1
6 + 127 = 133 -> 1000 0101 (8bit 지수부)
위 8비트를 지수부로 설정합니다.
∴ -118.625 = 1 10000101 11011010 10000000 0000000
메모리의 기본 입출력 단위를 아는가?
메모리 기본 입출력 단위는 1바이트이다.