boostcource
모두를 위한 컴퓨터 과학 (CS50 2019) : David J. Malan
www.boostcourse.org/cs11
실습환경 : https://sandbox.cs50.io/
하드웨어의 한계
컴퓨터에는 메모리 혹은 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 |