컴퓨터는 수를 근사로만 안다
지금까지 우리는 오차가 방법에서 온다고 봤다. 걸음이 너무 크다, 차수가 너무 낮다. 그런데 그 모든 방법 아래에는 더 근본적인 한계가 깔려 있어요. 컴퓨터는 실수를 정확히 저장하지 못한다. 0.1 조차도 그렇다. 64비트 부동소수점은 약 15~16자리만 기억하고 나머지는 반올림해 버려요. 보통은 이 미세한 오차가 묻혀 안 보이지만, 거의 같은 두 수를 빼거나 작은 오차를 수백만 번 쌓거나 답이 입력에 민감한 나쁜 조건수를 만나면 갑자기 튀어나온다. 이 강은 수치해석의 바닥, 곧 컴퓨터가 수를 다루는 방식 자체와 거기서 새는 오차를 들여다본다. 앞 강의 오차 바닥(roundoff floor)이 어디서 왔는지 그 정체예요.
수직선 위 눈금이 컴퓨터가 정확히 저장할 수 있는 수다. 첫눈에 이상하다. 0 근처는 촘촘한데 오른쪽으로 갈수록 성겨져요. 부동소수점은 마치 과학적 표기법처럼 유효숫자 몇 개에 지수를 붙여 수를 적기 때문이다. 그래서 크기가 두 배가 될 때마다 이웃한 표현값 사이의 간격도 딱 두 배가 된다. 이 최소 간격을 ulp(units in the last place)라고 불러요. x 를 옮겨 보면 큰 수일수록 ulp 가 커지는 게 보인다. 큰 수는 거칠게, 작은 수는 곱게 저장된다는 뜻이다. 모든 반올림오차가 여기서 시작해요.
그 간격이 실제로 얼마나 작은지 1 근처에서 잰다. ε = 2k 를 1 에 더하면서 k 를 점점 내리면, 어느 순간 1+ε 가 그냥 1 이 돼 버려요. 더한 ε 가 1 위쪽 표현 간격의 절반보다 작아지면 가장 가까운 표현값인 1 자신으로 반올림되기 때문이다. 이 경계가 머신 엡실론이고, float64 에서는 2⁻⁵² ≈ 2.2×10⁻¹⁶ 이다. 약 15~16 유효숫자에 해당한다. 이건 추상적인 얘기가 아니라 진짜 계산이에요. 컴퓨터에게 1 더하기 아주 작은 수는 그냥 1 일 수 있다. 정밀도에는 바닥이 있다는 첫 증거예요.
오차 하나는 10⁻¹⁶ 수준이라 무시해도 될 것 같다. 그런데 0.1 은 이진법으로 딱 떨어지지 않아서 저장하는 순간 이미 아주 작은 오차를 품는다. 그걸 한 번 더할 때마다 오차도 조금씩 같이 쌓여요. 슬라이더로 더한 횟수를 늘리면 참값 n/10 에서 벌어지는 오차가 차곡차곡 커진다. 한 번엔 안 보이던 게 수백 번이면 또렷해지고, 시뮬레이션처럼 수십억 번 반복하는 계산에서는 결과를 망칠 수도 있어요. 그래서 수치 프로그래머는 더하는 순서를 작은 것부터로 바꾸거나 카한 합 같은 보정 합산 기법으로 이 누적을 다스린다.
가장 무서운 건 따로 있다. 거의 같은 두 수를 빼는 것이다. (1−cos x)/x² 를 작은 x 에서 계산해 본다. 참값은 x 가 작아질수록 0.5 로 가야 한다. 그런데 곧이곧대로 계산한 금색 곡선은 어느 순간 0 으로 무너져요. x 가 작으면 cos x 가 거의 1 이라 1−cos x 가 유효숫자를 거의 다 잃어버리기 때문이다. 앞자리가 다 같아서 지워지고 믿을 수 없는 뒷자리만 남는 것이다. 이게 치명적 상쇄예요. 파란 곡선은 같은 값을 1−cos x = 2 sin²(x/2) 로 바꿔 계산한 것이다. 빼기를 아예 안 하니까 0.5 를 끝까지 지킨다. 수식은 같아도 계산 순서가 정밀도를 살리고 죽여요.
마지막은 오차가 아니라 문제 자체의 성격이다. 연립방정식을 푸는 건 두 직선의 교점을 찾는 일이다. 두 직선이 시원하게 가로지르면 교점이 또렷해요. 그런데 거의 나란하면 한 직선을 머리카락만큼 흔들어도 교점이 저 멀리 휙 튄다. 슬라이더로 기울기를 줄여 두 직선을 나란하게 만들면 같은 10% 흔들림에 해가 점점 크게 움직여요. 이 증폭 배율이 조건수다. 조건수가 크면 입력의 작은 오차가 답에서 크게 부푸는 나쁜 조건이 된다. 무서운 건 이게 반올림오차와 달리 더 좋은 컴퓨터로도 못 고친다는 점이다. 문제 자체가 예민하기 때문이에요. 그래서 좋은 수치해석은 안정한 알고리즘과 잘 조건된 문제, 둘 다를 챙긴다.