들어가기 전에
컴퓨터에서 프로그램의 정보들이 저장되는 곳은 메모리다.
메모리의 용량은 한정되어 있기 때문에 컴퓨터가 할 수 있는 일에도 근본적인 한계가 있다.
저장 공간이 유한하므로 저장 가능한 숫자 역시 한계가 있고, 당연히 연산에도 한계가 있을 수 밖에 없다.
부동 소수점 부정확성
실수 x, y를 인자로 받아 x 나누기 y를 하는 프로그램을 생각해보자.
#include <cs50.h>
#include <stdio.h>
int main(void){
// 사용자에게 x,y값 받기
float x = get_float("x: ");
float y = get_float("y: ");
// 나눗셈 후 출력
printf("x/y = %.50f\n", x/y);
}
%.50f는 나눈 결과를 소숫점 50자리까지 출력하는 코드이다.
x에 1을 y에 10을 넣으면 위와 같은 결과가 나온다. 우리가 수학 시간에 배운 것으로는 분명 0.1000000000...이 나와야 하는데 왜 위와 같은 결과가 나온 것일까?
이것은 float에서 저장 가능한 비트 수가 정해져있기 때문에 부정확한 결과를 내기 때문이다.
정수 오버플로우
비슷한 오류로, 1부터 시작해서 2를 계속 곱하여 출력하는 프로그램이 있다고 하자.
#include <stdio.h>
#include <unistd.h>
int main(void){
for (int i=1;;i*=2){
printf("%i\n", i);
//출력 후 1초 쉬도록
sleep(1);
}
}
우리는 i를 int로 저장하기 때문에, 2를 계속 곱하다가 int타입이 저장할 수 있는 수를 넘은 이후에는 에러가 발생하게 될 것이다.
int형의 범위는 -2,147,483,648 ~ 2,147,483,647 인데, 1,073,741,824 * 2가 int형의 범위를 넘어가므로 앞으로 넘어갈 1의 자리가 없어진 것이다. int에서는 32개의 비트가 다였기 때문에 그 이상의 숫자는 저장할 수 없는 것이다.
이것을 좀 더 쉽게 풀어 쓰자면,
우리에게 딱 1bit가 허락 된다고 하자
0
1
2
3
4
5
..
9
까지는 잘 갈 것이다. 하지만 10이 되는 순간 오버플로우로 인해 앞으로 가야할 1이 사라지면서 다시 0으로 돌아온다.
실생활의 오버플로우 문제
Y2K 문제는 연도를 1998 이렇게 네자리가 아닌, 98과 같은 형식으로 마지막 두 자리수로 저장했던 관습 때문에 생긴 문제다. 1999년에서 2000년으로 넘어갈 때, 99에서 00으로 정수 오버플로우가 발생하면서 새해가 2000년이 아닌 1900년으로 인식된다는 문제였다. 세계는 수백만 달러를 투자해서 프로그래머들에게 더 많은 메모리를 활용해 이 문제를 해결하도록 했다.