2부. 세는 세계와 재는 세계
앞 장에서 우리는 왜 컴퓨터 안에 계산기가 둘인지, 그리고 FPU가 왜 하나의 타협처럼 등장했는지를 봤다. 이제 한 단계 더 들어가 보자. 다음 문제를 생각해보자.
부동소수점의 한계는 단지 회로가 덜 정교해서 생기는가, 아니면 애초에 피하기 어려운 구조적 비용인가?
세는 수와 재는 수
Section titled “세는 수와 재는 수”먼저 가장 직관적인 구분부터 다시 잡자.
- 사과 5개, 칩 32개, 셀 128개처럼 몇 개인지를 말할 때 우리는 숫자를
센다. - 길이 1.73미터, 속도 초속 3.2미터, 온도 22.4도처럼 양의 크기를 말할 때 우리는 숫자를
잰다.
이 단순한 구분은 의외로 계산 체계의 성격을 꽤 정확하게 드러낸다.
- 정수는 세는 세계와 잘 맞는다.
- 실수는 재는 세계를 표현하는 데 강하다.
수학에서는 보통 앞쪽을 이산(discrete), 뒤쪽을 연속(continuous)이라고 부른다.
외울 필요는 없고 감각만 기억해두자.
이산은 하나씩 셀 수 있고 중간이 비어 있다.연속은 값과 값 사이에 또 다른 값이 무한히 끼어들 수 있다.
정수 3과 4 사이에는 다른 정수가 없다.
하지만 3.0과 4.0 사이에는 3.1, 3.14, 3.1415처럼 계속 값이 들어간다.
이 차이가 뒤에서 오차의 뿌리가 된다.
인간은 왜 재는 언어를 택했는가
Section titled “인간은 왜 재는 언어를 택했는가”우리가 사는 세계는 대개 세는 것보다 재는 것을 선호한다. 물체는 움직이고, 온도는 변하고, 압력은 오르내리고, 전압은 흔들린다. 이런 현상은 불연속적으로 끊긴 값보다 매끈한 곡선과 변화율로 표현할 때 훨씬 이해하기 쉽다.
그래서 인간은 오랫동안 연속적인 수학의 언어를 발전시켜 왔다. 미적분이 강력한 이유도 여기에 있다. 미분과 적분은 변화와 누적을 하나의 언어 안에서 매끈하게 다룰 수 있게 해 준다.
여기까지는 문제가 없다.
실제로 이 언어는 너무 잘 작동했다.
문제는 그 다음이다.
그 언어를 계산 기계 위에 올려놓는 순간, 컴퓨터는 연속 세계를 직접 다루지 못한다.
컴퓨터는 끝까지 이산적인 기계다
Section titled “컴퓨터는 끝까지 이산적인 기계다”컴퓨터 안에는 무한히 촘촘한 값이 없다. 레지스터도 메모리도 결국 유한한 비트의 묶음이다. 상태는 끝까지 0과 1의 조합으로 저장된다. 이 점에서 컴퓨터는 본질적으로 이산적인 기계다.
그래서 컴퓨터가 실수를 다룬다는 말은 정확히 말하면 이런 뜻이다.
연속적인 값을 직접 다루는 것이 아니라, 연속적인 것처럼 보이는 표현을 유한한 비트에 근사해서 올려놓는다.
앞 장에서 말한 ALU와 FPU의 분리는 여기서 다시 해석된다.
- ALU는 이산적인 비트 세계와 자연스럽게 맞물린다.
- FPU는 그 위에 연속적인 수를 흉내 내기 위한 ‘별도 표현 규칙’을 얹는다.
즉 FPU의 한계는 “복잡한 회로라서 힘들다” 수준이 아니라, 이산적인 기계 위에서 연속적인 언어를 번역하는 비용과 연결된다.
0.1은 왜 정확히 담기지 않는가
Section titled “0.1은 왜 정확히 담기지 않는가”이 말을 가장 짧게 보여주는 예가 0.1이다.
10진수에서 0.1은 너무 익숙해서 딱 떨어지는 값처럼 느껴진다. 하지만 이 값을 컴퓨터가 다루는 이진수로 옮기면 끝이 나지 않는다. 1/10이 2의 거듭제곱 분모로 정확히 떨어지지 않기 때문이다. 그래서 컴퓨터는 0.1을 저장할 때, 정확한 0.1이 아니라 가장 가까운 어떤 근사값을 저장한다.
예를 들어, float에서는 대략 이렇게 저장된다.
이 사실 하나로 이미 중요한 결론이 나온다.
- 부동소수점은 애초부터 실수를 정확히 담는 그릇이 아니다.
- 시작점부터 이미 근사 표현이다.
즉 오차는 나중에 생기는 사고가 아니라, 표현 단계에서부터 조용히 들어와 있다.
오차는 왜 누적되고, 왜 순서에 민감한가
Section titled “오차는 왜 누적되고, 왜 순서에 민감한가”근사값을 가지고 계산하면 그 다음 연산도 근사값 위에서 진행된다. 여기서 여러 문제가 구조적으로 따라온다.
첫째, 반올림 오차 누적이다.
한 번의 오차는 미세해 보여도, 같은 종류의 연산이 길게 반복되면 그 오차가 계속 전파된다.
둘째, 계산 순서 민감성이다. 수학적으로는 같은 식이라도, 기계에서는 중간 반올림 지점이 달라지면 결과가 조금씩 달라질 수 있다. 큰 수와 작은 수를 어떤 순서로 더하는지, 중간에 어떤 값이 먼저 소거되는지에 따라 결과가 달라지는 이유가 여기에 있다.
셋째, 재현성 문제다.
같은 알고리즘이라도 하드웨어, 최적화, 병렬 실행 순서가 바뀌면 마지막 비트가 달라질 수 있다.
이것은 “컴퓨터가 틀렸다”기보다, 근사 표현과 반올림 규칙 위에서 계산을 조직했기 때문에 생기는 현상이다.
이쯤 오면 앞 장의 “타협의 대가”가 더 선명해진다. FPU는 현실의 수를 계산 가능하게 해 주었지만, 그 대가로 오차를 끝까지 관리해야 하는 체계를 함께 들여왔다.
이것은 기술 부족의 문제가 아니다
Section titled “이것은 기술 부족의 문제가 아니다”여기서 흔히 나오는 반응이 있다. “비트폭을 더 넓히면 되는 것 아닌가?” 내지는, “회로를 더 정교하게 만들면 되는 것 아닌가?”
물론 정밀도를 높이면 상황은 나아진다. 더 많은 비트를 쓰면 근사 오차를 더 작게 만들 수 있다. 하지만 중요한 건 “작아진다”이지 “사라진다”가 아니라는 점이다.
연속적인 값을 유한한 비트로 표현하는 한,
- 표현 가능한 값은 유한하고
- 그 사이에는 빈틈이 생기며
- 어떤 값은 반드시 근사로 저장되고
- 그 근사 위에서 계산한 결과도 다시 근사로 남는다.
즉 부동소수점의 한계는 조잡한 구현의 부산물이 아니라, 연속적인 세계를 유한한 기계에 태우는 방식 자체에서 오는 숙명에 가깝다.
그래서 우리가 겨누는 것은 무엇인가
Section titled “그래서 우리가 겨누는 것은 무엇인가”이제 담론 2의 문제의식도 좀 더 선명해진다. 이 시리즈는 “실수는 틀렸고 정수만 옳다”는 식의 단순한 선언을 하려는 것이 아니다. 연속의 언어는 여전히 강력하고, 현대 과학과 공학은 그 언어 위에서 크게 발전해 왔다.
문제는 둘 중 어느 쪽이 더 고급이냐가 아니다. 진짜 문제는 이것이다.
연속의 언어를 이산 기계 위에 태우는 비용을 언제까지 계산의 기본값처럼 받아들여야 하는가?
바로 이 지점에서 다음 장의 질문이 열린다.
실수를 더 정교하게 근사하는 방향만이 과연 유일한 길인가?
다음 장은 연속처럼 읽히는 변화를 더 아래쪽의 정수 규칙에서 다시 세울 수 있느냐를 묻는다.
- 정수와 실수의 차이는 단순한 교과서 분류가 아니라, 세는 세계와 재는 세계의 차이다.
- 컴퓨터는 본질적으로 이산적인 기계이기 때문에, 연속적인 값을 다룰 때 번역 비용과 계산 비용을 함께 감당한다.
- 부동소수점 오차는 단순한 구현 실수가 아니라, 연속적인 값을 유한한 비트에 가두는 설계에서 따라붙는 구조적 비용이다.