boostcource
모두를 위한 컴퓨터 과학 (CS50 2019) : David J. Malan
www.boostcourse.org/cs11
실습환경 : https://sandbox.cs50.io/
모두를 위한 컴퓨터 과학 (CS50 2019)
부스트코스 무료 강의
www.boostcourse.org
하드웨어의 한계
컴퓨터에는 메모리 혹은 RAM(Random Access Memory)이라고 칭하는 물리적 저장 장치가 있다.
우리가 작성한 모든 프로그램은 실행 중에 RAM에 저장된다.
쉽게 말하자면 컴퓨터가 여러 일들을 한 번에 할 때 기억하기 위해 사용한다.
다만 이 저장 공간은 유한하다.
1GB, 4GB, 8GB와 같이 메모리는 저장할 수 있는 크기의 한계를 갖는다.
즉, 컴퓨터가 할 수 있는 일에는 근본적인 한계가 있다.
부동 소수점 부정확성
이처럼 연산에서도 한계가 존재한다.
아래는 사용자로부터 몇 개의 실수 값을 받아오는 예시이다.
#include <cs50.h>
#include <stdio.h>
int main(void){
float x = get_float("x: ");
float y = get_float("y: ");
printf("x / y = %f\n", x/y);
}
// ? x:
// > 1
// ? y:
// > 10
// ? x / y = 0.100000
실수(float)를 불러오기 위한 형식 지정자 %f 를 사용하면 소수점 아래 6자리 노출이 기본적이다.
때문에 1 / 10 의 결과로 0.100000 이 출력된다.
만일 소수점 아래 1자리만 보이고 싶다면 %.1f 를 입력하면 된다.
int main(void){
float x = get_float("x: ");
float y = get_float("y: ");
printf("x / y = %.1f\n", x/y);
}
// ? x: 1
// ? y: 10
// ? x / y = 0.1
그리고 만일 소수점 아래 10자리를 보고 싶다면? %.10f 를 입력하면 된다.
int main(void){
float x = get_float("x: ");
float y = get_float("y: ");
printf("x / y = %.10f\n", x/y);
}
// ? x: 1
// ? y: 10
// ? x / y = 0.100000015
엥 근데 결과가 이상하다. 0.100000000 가 아닌 0.100000015 ?
우연일지 모르니 이번에는 50자리를 출력해보자.
int main(void){
float x = get_float("x: ");
float y = get_float("y: ");
printf("x / y = %.50f\n", x/y);
}
// ? x: 1
// ? y: 10
// ? x / y = 0.10000000149011611938476562500000000000000000000000
?????
0.10000000149011611938476562500000000000000000000000 라는 대단한 숫자가 나왔다.
이게 무슨 일일까?🤔
앞서 하드웨어의 한계를 다시 설명하자면
RAM은 유한한 크기의 비트만 저장할 수 있기 때문에 때때로 부정확한 결과를 내기도 한다.
float도 동일하다.
float도 저장 가능한 비트 수가 유한하기 때문에 다소 부정확한 결과를 내는 것이다.
덧붙여 보여주자면, 이전에 자료형을 공부할 때
float은 실수.
double은 float보다 소수점 아래 더 많은 숫자를 가지는 실수.
라는 표현으로 가볍게 정리했었으나
정확한 수치로 말하자면
float은 32비트를 사용해 실수를 표현하고,
double은 64비트를 사용해 실수를 표현한다.
double은 float의 2배수 비트를 사용하기 때문에 조금 더 범위가 크다.
때문에 위 %.50f 예제에서 float으로 선언한 x와 y를 double로 선언한다면 x/y 값이 조금 달라진다.
int main(void){
double x = get_float("x: ");
double y = get_float("y: ");
printf("x / y = %.50f\n", x/y);
}
// ? x: 1
// ? y: 10
// ? x / y = 0.10000000000000000555111512312578270211815834045410
- float: 0.10000000149011611938476562500000000000000000000000
- double: 0.10000000000000000555111512312578270211815834045410
여전히 소수점 아래 50자리를 담기에는 작지만
float과 비교했을 때 표현할 수 있는 소수점 아래 자릿수를 조금 더 안정적으로 표현한다.
이 현상에 대해
컴퓨터가 유한한 정보를 사용하면서 계산할 수 있는 값들 중
1/10에 가장 가까운 값을 저장한 것이라고 해석할 수 있다.
정수 오버플로우
위와 비슷한 예시를 정수로 확인해보기 위해 다음과 같은 코드를 실행해보자.
#include <stdio.h>
#include <unistd.h>
int main(void){
for(int i=1; ; i*=2){ // 한계를 표현하지 않음 = 영원히 반복
printf("%i\n", i);
sleep(1); // 1초씩 쉬어감
}
}
1부터 시작한 정수 i에 대해 2를 곱한 값을 1초마다 콘솔창에 출력해 나갈 것이다.
결과를 확인해보자.
// ? 1
// ? 2
// ? 4
// ......
// ? 1073741824
// ? overflow.c:6:25: runtime error: signed integer overflow: 1073741824 * 2 cannot be represented in type 'int'
// ? -2147483648
// ? 0
// ? 0
// ......
?????????
잘 가다가 어느 순간
overflow.c:6:25: runtime error: signed integer overflow: 1073741824 * 2 cannot be represented in type 'int'
라는 경고와 함께 0만 출력되는 현상이 발생한다.
10억까지 계속해서 숫자를 키워나가다가
나중에는 앞자리에 1을 더할 비트 조차 없어져 표현할 수 없게 되는 것이다.
int 자료형 역시 32개의 비트가 전부이기 때문이다.
이렇게 부동 소수점 부정확성과 정수 오버플로우를 통해서
다루고자 하는 데이터 값의 범위를 유의하며 프로그램을 작성해야 한다는 점을 알 수 있다.
'CS > CS50' 카테고리의 다른 글
[CS][CS50] 배열 - 배열 (0) | 2023.05.29 |
---|---|
[CS][CS50] 배열 - 컴파일링 / 디버깅 / 코드의 디자인 (0) | 2023.05.26 |
[CS][CS50] C언어 - 사용자 정의 함수, 중첩 루프 (0) | 2023.05.24 |
[CS][CS50] C언어 - 자료형, 형식 지정자, 연산자 (0) | 2023.05.09 |
[CS][CS50] C언어 - C 기초 / 문자열 / 조건문과 루프 (0) | 2023.04.25 |