[C언어] 하드웨어의 한계

woodstock·2024년 2월 14일
post-thumbnail

컴퓨터는 우리가 작성한 프로그램을 구동하기 위해 다양한 물리적 장치를 사용한다.

그 중 하나인 메모리는 프로그램이 필요한 정보가 저장되는 곳으로, 용량이 무한하지 않기 때문에 프로그램에서 우리가 의도하지 않은 오류가 발생하기도 한다.

컴퓨터는 RAM(랜덤 액세스 메모리)이라는 물리적 저장장치를 포함하고 있다.

우리가 작성한 프로그램은 구동 중에 RAM에 저장되는데, RAM은 유한한 크기의 비트만 저장할 수 있기 때문에 때때로 부정확한 결과를 내기도 한다.

부동 소수점 부정확성

아래와 같이 실수 x, y를 인자로 받아 x 나누기 y를 하는 프로그램이 있다고 해보자.

#include <cs50.h>
#include <stdio.h>

int main(void)
{
    // 사용자에게 x 값 받기
    float x = get_float("x: ");

    // 사용자에게 y 값 받기
    float y = get_float("y: ");

    // 나눗셈 후 출력
    printf("x / y = %.50f\n", x / y);
}

나눈 결과를 소수점 50자리까지 출력하기로 하고, x에 1을, y에 10을 입력하면 아래와 같은 결과가 나온다.

정확한 결과는 0.1이 되어야 하지만, float 에서 저장 가능한 비트 수가 유한하기 때문에 다소 부정확한 결과를 내게 되는 것이다.

정수 오버플로우

비슷한 오류로, 1부터 시작하여 2를 계속해서 곱하여 출력하는 아래와 같은 프로그램이 있다고 해보자

#include <stdio.h>
#include <unistd.h>

int main(void)
{
    for (int i = 1; ; i *= 2)
    {
        printf("%i\n", i);
        sleep(1);
    }
}

우리가 변수 iint로 저장하기 때문에, 2를 계속 곱하다가 int 타입이 저장할 수 있는 수를 넘은 이후에는 아래와 같은 에러와 함께 0이 출력될 것이다.

정수를 계속 키우는 프로그램에서 10억을 넘기자 앞으로 넘어갈 1의 자리가 없어진 것이다.

int에서는 32개의 비트가 다였기 때문에 그 이상의 숫자는 저장할 수 없는 것이다.

이런 오버플로우 문제는 실생활에서도 종종 발견된다.

사례 1. Y2K

1999년에 큰 이슈가 되었던 Y2K 문제는 연도를 마지막 두 자리수로 저장했던 관습 때문에 새해가 오면 ‘99’에서 ‘00’으로 정수 오버플로우가 발생하고, 새해가 2000년이 아닌 1900년으로 인식된다는 문제였다.

그리고 세계는 수백만 달러를 투자해서 프로그래머들에게 더 많은 메모리를 활용해서 이를 해결하도록 하였다.

이는 통찰력 부족으로 발생한 아주 현실적이고 값비싼 문제였다.

사례 2. 보잉 787

비행기 보잉 787에서 구동 후 248일이 지나면 강제로 안전 모드로 진입하여 모든 전력을 잃는 문제가 있었다.

이는 소프트웨어의 변수가 248일이 지난 뒤에 오버플로우가되어 발생하였기 때문이었다.

248일을 1/100초로 계산하면 대략 2의 32제곱이 나오는데, 보잉을 설계할때 사용한 변수보다 너무 커졌던 것이다.

이를 해결하기 위해 보잉은 주기적으로 재가동을 하여 변수를 다시 0으로 리셋했다.

위와 같은 사례로 보아, 다루고자 하는 데이터 값의 범위를 유의하며 프로그램을 작성하는 것이 중요하다는 것을 알 수 있다.


생각해보기

Y2K와 보잉787과 같은 문제를 방지하기 위해서는 프로그램을 어떻게 설계해야 할까?

정답

  1. 적절한 데이터 타입 선택 :
    변수가 충분히 큰 범위의 값을 저장할 수 있도록 적절한 데이터 타입을 선택한다.

  2. 미래 지향적 설계 :
    미래의 확장성과 유연성을 고려하여 시스템을 설계한다.

  3. 정기적인 리뷰와 테스트 :
    시스템을 정기적으로 검토하고 테스트하여 잠재적인 문제를 사전에 발견하고 수정한다.

  4. 경계 조건 검사 :
    데이터의 최대값과 최소값에 대한 경계 조건을 명확히 정의하고 검사한다.

  5. 문서화와 지식 공유 :
    설계 결정과 알려진 제한 사항에 대해 문서화하고 공유한다.

  6. 유연한 아키텍처와 업데이트 :
    필요에 따라 쉽게 업데이트하거나 확장할 수 있는 유연한 아키텍처를 사용한다.

이렇게 하면 시스템의 장기적인 안정성과 신뢰성을 보장할 수 있다.

profile
해내는 사람

0개의 댓글