머신 엡실론(Machine Epsilon) 이해

머신 엡실론의 기초 정의

실수 연산을 컴퓨터로 처리할 때 가장 먼저 고려해야 할 문제는 유한 비트로 표현되는 부동소수점 수 체계에서 발생하는 근사와 반올림이다. 컴퓨터 내부의 연산은 실제 수학적 실수 연산과 동일하지 않으므로, 소수점 아래로 무한히 늘어날 수 있는 진법 전개를 모두 수용하지 못한다. 따라서 특정한 포맷으로 표현 가능한 근사값만 다룰 수 있으며, 이로 인해 생기는 대표적 개념이 머신 엡실론이다.

머신 엡실론은 어떤 부동소수점 표현 체계에서 1에 가장 가깝게 표현될 수 있는 숫자를 하나 증가시켰을 때, 그 차이를 실수 연산상으로 지칭한 것이다. 이를 좀 더 엄밀히 표현하면, 부동소수점 연산에서 $1 + \varepsilon$을 계산했을 때 $1$과 구분되는 결과가 처음으로 나타나는 최소의 $\varepsilon$ 값이 머신 엡실론이다. 예를 들어, IEEE 754 배정도(double precision)에서 약 $2^{-52}$ 정도가 머신 엡실론과 동일하다는 것이 알려져 있다.

IEEE 754 표준의 배정도 방식을 예로 들면, 배정도 부동소수점 수는 실질적으로 53비트의 유효숫자(significand)를 가진다. 이때, 가수부의 최상위 비트는 숨겨진 1(hidden bit)로 취급하므로, 정상화(normalized)된 부동소수점 수에서 실제로 표현되는 유효숫자 부분은 1.xxxxx...의 꼴이 된다. 따라서 1에서 1 비트만큼 유효숫자가 바뀌어야 구별이 가능한데, 그 간격이 $2^{-52}$라는 점이 핵심이다. 이를 기호적으로 쓰면 아래와 같은 관계가 성립한다.

Machine Epsilon252\text{Machine Epsilon} \approx 2^{-52}

머신 엡실론의 존재 의의

머신 엡실론은 부동소수점 연산 정밀도를 해석하는 데 핵심적인 기준점 역할을 한다. 컴퓨터가 인지할 수 있는 최소 간격이 어디까지인지 알려주므로, 예측하지 못한 오차 전파가 발생할 가능성을 사전에 검토할 수 있다. 예를 들어, $a$와 $b$가 서로 매우 큰 스케일을 갖고 있을 때 $a + b \approx a$가 되어버리는 상황을 가늠해볼 수 있다. 이는 $b$가 $a$에 비해 머신 엡실론 수준으로 작아지면, 반올림 과정에서 $a + b$와 $a$가 같은 부동소수점 값으로 저장되기 때문이다.

머신 엡실론은 알고리즘의 안정성과 오차 해석에도 직접적으로 활용된다. 예를 들어, 어떤 알고리즘이 계산 과정에서 발생하는 오차를 제어하려 할 때, 머신 엡실론 규모 이하의 차이는 반올림 효과로 인해 검출되지 않을 수 있음을 유념해야 한다. 신뢰도 높은 수치해석을 설계하려면, 이러한 머신 수준의 한계를 간과하지 않는 세밀한 오차 분석이 필수적이다.

구체적 표현과 일반화

일반적으로 2진 부동소수점 시스템에서 기수(base)는 2다. 배정도 방식에서 가수부 비트 수를 $p$라 하면, 대략적인 머신 엡실론은 $2^{1-p}$로 표현된다. IEEE 754 배정도 체계는 $p=53$(숨겨진 비트 포함)이므로, 머신 엡실론은 $2^{-52}$가 된다. 반면, 단정도(single precision)의 경우 $p=24$이므로, 머신 엡실론은 $2^{-23}$ 정도가 된다. 시스템에 따라 내부 표현이 다를 수도 있지만, IEEE 754가 사실상 표준으로 자리 잡았으므로, 일반적인 데스크톱이나 서버 환경에서는 이 표준을 따른다고 봐도 무방하다.

부동소수점 수는 다음과 같은 형태로 표현할 수 있다.

x=±(1.b1b2bp1)2×2ex = \pm (1.b_1b_2\dots b_{p-1})_2 \times 2^{e}

여기서 $b_i$는 0 또는 1인 이진수로, 총 $p-1$개로 이루어진 가수부를 구성한다. 항상 정상화 형태(leading bit가 1인 형태)로 표현하므로, $1$과 $(1 + 2^{-p+1})$ 사이의 가장 작은 간격이 머신 엡실론이다.

상기 정의에서 주목할 점은, $\varepsilon$이 단순히 "1과 1이 아닌 수"의 차이가 아니라, "정상화된 부동소수점에서 인지 가능한 최소 양의 차이"라는 데에 있다. 실제 계산 환경에서 부동소수점 연산이 어떻게 정규화(normalization) 과정을 거쳐 반올림되는지 이해하려면, 내부 바이너리 구조뿐 아니라 반올림 모드(올림, 버림, 근접 값 등)에 따른 차이도 살펴볼 필요가 있다.

반올림 모드와 머신 엡실론

부동소수점 연산에서는 반올림 모드에 따라 결과가 달라진다. 대표적으로는 "가장 가까운 수로 반올림하되, 정확히 중간이면 짝수 쪽으로"라는 방식(round to nearest, ties to even)이 쓰인다. 이때도 1에 $2^{-52}$를 더했을 때와 더하지 않았을 때가 구분되어 저장되어야 하므로, 근본적인 머신 엡실론의 정의는 변하지 않는다. 다만 반올림 모드에 따라 임계값 경계가 달라질 수 있으므로, 부동소수점 산술을 다루는 알고리즘 설계 시 이를 명심해야 한다.

머신 엡실론 자체는 1을 기준으로 한 간격이므로, 수가 커지거나 작아질수록 실제로 구분될 수 있는 간격은 지수부($e$)에 의해 달라진다. 예컨대 매우 큰 수 영역에서는 부동소수점 스케일이 커지므로, 단일 머신 엡실론보다 훨씬 큰 값이 되어서야 양자화(quantization) 단계를 건너뛰게 된다. 반대로 매우 작은 수영역(서브노멀 영역)에서는 1이 아니라 0을 기준으로 한 정밀도 제한이 작용한다. 따라서 머신 엡실론은 항상 일정한 절대적 간격을 뜻하지 않고, 상대 정밀도(relativel precision)를 위한 기준이라고 보는 것이 더욱 정확하다.

머신 엡실론을 측정하는 프로그램 예시

머신 엡실론의 개념을 직접 코드로 구현해보면 더욱 명확해진다. 예를 들어, 부동소수점에서 $1 + \varepsilon$이 1과 구분되지 않을 정도로 작은 $\varepsilon$을 찾고, 그 다음 단계에서 2배를 해주어 실제 머신 엡실론 값을 추정할 수 있다. 아래는 Python 예시다.

이 코드를 실행하면 약 $2^{-52}$에 가까운 값이 출력된다. 이는 IEEE 754 배정도(double precision) 환경에서의 머신 엡실론을 경험적으로 측정한 결과와 일치한다. 이 방식은 단정도(single precision) 환경에서도 동일하게 적용할 수 있지만, 결과값은 $2^{-23}$ 정도가 된다.

알고리즘이 동작하는 원리를 간단히 살펴보면, mach를 계속 절반으로 줄여가며 $1.0 + mach$가 1과 같은 수로 반올림되는 순간을 찾는다. 즉,

1.0+mach=1.01.0 + mach = 1.0

이 되는 지점을 지나면, 이미 mach가 너무 작아서 부동소수점 체계에서 분해능(resolution)을 벗어나게 된다. 그 직전 단계의 mach가 실제로 $1 + \varepsilon$을 구분할 수 있는 범위였다. 따라서 마지막에 mach를 2배 해주어 그 임계값을 계산한다.

머신 엡실론과 ULP(Unit in the Last Place)

부동소수점 수 체계에서 흔히 언급되는 개념 중 하나가 ULP(마지막 자리 단위)이다. 특정 부동소수점 수 $x$에서 바라본 ULP는, 그 수를 표현하는 이진 포맷에서 가장 낮은 자리수 한 칸이 변했을 때의 차이를 뜻한다. 예컨대 $1$에서의 ULP는 머신 엡실론과 동일하게 $2^{-52}$가 된다. 그러나 2와 같은 수에 대해서는 $2 + 2^{-52}$가 그다음 수가 아니라 $2 + 2^{-51}$가 다음 수가 되므로, 그 ULP는 $2^{-51}$로 달라진다.

일반적으로 $x$에서의 ULP는 $x$가 가진 지수부 $e$에 의존한다. 이로 인해 실제로 어떤 값에서 소수점 한 스텝을 건너뛰는 양자화 단계가 수에 따라 달라진다. 머신 엡실론은 $1$에서의 ULP를 정의한 값이지만, 알고리즘의 정확도나 오차 분석을 할 때에는 계산 중 발생하는 수의 스케일을 함께 살펴야 한다.

머신 엡실론과 연산 오차

부동소수점 연산 오차는 반올림 과정에서 머신 엡실론 크기의 간격을 기준으로 나타난다. 임의의 실수 $x, y$가 주어졌다고 할 때, 부동소수점 연산 $\oplus$가 실제 연산과 달라지는 지점은 크게 두 가지로 구분할 수 있다.

하나는 절대 오차의 형태로, 연산 결과 $\tilde{z}$와 실제 결과 $z = x + y$의 차이가 일정 간격을 넘는지를 보는 방식이다. 다른 하나는 상대 오차로, 실제 값에 대한 비율 $\frac{|\tilde{z} - z|}{|z|}$가 머신 엡실론 수준에서 통제되는지 확인하는 방식이다. 보통 부동소수점 연산에서는 상대 오차 기준으로 분석하는 경우가 많다. 수가 매우 커지면 절대 오차가 증가해도 상대 오차는 $O(\varepsilon)$ 범위에 머무를 수 있기 때문이다.

이에 따라, 대규모 과학기술 계산에서 오차가 누적될 때에는 알고리즘이 여러 단계의 연산을 어떻게 수행하는가를 면밀히 살펴봐야 한다. 연산 순서를 바꾸면 중간 단계에서 발생하는 반올림 오차가 달라질 수 있으며, 작은 수와 큰 수의 덧셈 또는 뺄셈에서 나타나는 집합적 효과(예: 대수적 취소)가 결과를 심각하게 왜곡할 수도 있다.

대수적 취소(Catastrophic Cancellation)와 머신 엡실론

머신 엡실론이 특히 강조되는 상황 중 하나가 바로 대수적 취소(cancellation error)이다. 서로 값이 유사한 두 수 $a$와 $b$를 뺄셈할 때, 예를 들어 $a \approx b$이고 $a > b$라고 하자. 그 차이 $a - b$는 실제로 매우 작은 값이다. 그러나 부동소수점 연산에서는 $a$와 $b$의 큰 부분이 많은 자리에서 일치하기 때문에, 이 부분이 소멸되어 사라지는 과정에서 상대 오차가 크게 확대될 수 있다.

즉,

(ab)0(a - b) \approx 0

인 상황에서, 결과 자체는 아주 작지만, 이 값의 상대적 정확도가 현저히 떨어지는 문제가 생긴다. 수치해석에서는 이것을 대수적 취소라고 부른다. 이런 상황이 발생하면, 결과값이 머신 엡실론보다도 작은 크기를 가지게 되어 반올림 오차의 비중이 급증한다. 이 때문에, $a \approx b$ 상황에서 직접적인 뺄셈을 회피하도록 식을 재구성하거나, 안정화 기법을 사용하도록 여러 알고리즘에서 권장하고 있다.

실수해석과 비교

실수해석 관점에서는 $1 + \varepsilon$과 1의 차이가 어느 정도인지, 나아가 $a - b$가 작은 경우 오차가 어떤 식으로 계산되는지 정확하게 알고 있다. 하지만 컴퓨터 연산에서는 머신 엡실론 규모로 제한된 정밀도 안에서 모든 실수를 근사 표현하므로, 예측하기 어려운 분포의 미세한 값들이 사라진다. 이는 순수한 수학적 해석과 대비되는 중요한 차이점이다.

서브노멀 수(Subnormal Numbers)와 정밀도

머신 엡실론은 정상화(normalized)된 부동소수점 수에서 정의되지만, 부동소수점 표준에서는 매우 작은 수 영역을 위해 서브노멀(subnormal) 수를 지원한다. 서브노멀 수는 가수부의 leading bit가 1로 고정되지 않는 형태다. 예를 들어, IEEE 754 배정도에서는 지수부가 최소값보다 더 작아야 할 때, 수를 완전히 0으로 내리기보다 가능한 한 작은 크기의 수로 표현하는 방식을 채택한다.

서브노멀 수 영역에 접어들면, 정밀도가 저하된다. 정상화된 부동소수점 수의 경우 가수부가 $1.b_1 b_2 \dots b_{p-1}$ 꼴인 반면, 서브노멀 수는 $0.b_1 b_2 \dots b_{p-1}$ 형태가 되므로, 앞의 1비트를 상실한다. 그 결과, 실효 유효숫자(bit) 수가 줄어들어 상대 정밀도가 떨어진다. 이 영역에서의 간격은 머신 엡실론과 동일하게 유지되지 않고, 점차 더 커지게 된다. 따라서 아주 작은 수들을 다루는 알고리즘을 구성할 때는, 서브노멀 수로 인한 오차가 일반적인 상황과는 다르게 확대될 수 있음을 고려해야 한다.

서브노멀 수가 등장하는 전형적인 상황은, 어떤 연산 결과가 매우 작아 정상화 범위를 벗어나는 경우다. 예컨대 두 작은 수의 곱이나 차가 소수점 이하로 극도로 많이 내려가면, 그 결과가 서브노멀이 되어 표현될 수 있다. 이때 비정상적 반올림이 발생하거나, 연산 결과의 상대 오차가 커질 위험이 생긴다. 반대로, 알고리즘에서 매우 작아진 값을 완전히 0으로 만들어버리면 큰 오차가 발생하는 경우가 있어, 서브노멀 수의 존재 여부가 계산 안정성에 유리하게 작동하기도 한다.

확장 정밀도(Extended Precision)와 내부 연산

어떤 CPU나 컴파일러 환경에서는 표준 배정도보다 더 높은 정밀도로 중간 연산을 수행하는 확장 정밀도 방식을 사용하기도 한다. 예를 들어, x86 아키텍처 계열의 FPU(Floating Point Unit)는 과거 80비트 내부 레지스터(이른바 "extended precision")를 제공했는데, 이로 인해 $p=64$ 수준의 가수부가 보장되었다. 다만, 연산이 끝난 결과를 메모리에 저장할 때는 64비트(IEEE 754 배정도)로 반올림되므로, 코드 최적화나 디버깅 시 미묘한 오차 차이가 발생하기도 한다.

확장 정밀도를 활용하면 머신 엡실론이 감소하여(즉, 더 작은 값을 분해 가능) 중간 계산 정확도가 향상된다. 하지만 결과를 최종적으로 64비트 배정도로 저장할 때 한 번 더 반올림을 거치므로, 실제 알고리즘 구현에서는 이러한 추가 반올림 과정을 어떻게 다룰지 고민이 필요하다. 컴파일러의 최적화 옵션이나 특정 하드웨어 명령어가 확장 정밀도 사용 여부를 결정하므로, 동일한 소스 코드라도 시스템이나 빌드 옵션에 따라 미세한 차이가 날 수 있다.

머신 엡실론과 알고리즘 안정성

머신 엡실론을 비롯한 부동소수점 정밀도 개념은 알고리즘 안정성(algorithmic stability) 평가에서 빠질 수 없는 요소다. 어떤 알고리즘이 주어진 입력에 대해 연산 순서를 바꾸어도 결과 오차가 크게 달라지지 않는다면, 해당 알고리즘은 수치적으로 안정하다고 볼 수 있다. 반면, 입력에 아주 작은 변화만 주어도 결과가 크게 달라지거나, 계산 단계에서 대수적 취소가 빈번히 일어나면 불안정하다고 평가한다.

안정성 분석에서는 머신 엡실론이 오차의 기본 단위로 활용된다. 예를 들어, 어떤 단계를 거칠 때마다 최대 $C \cdot \varepsilon$ 정도의 상대 오차가 축적된다는 식으로 추정하기도 한다. 여기서 $C$는 알고리즘 구조나 중간 결과의 크기에 따라 달라지는 상수 계수다. 이를 통해 전체 연산을 마친 최종 결과가 이론적으로 어느 정도의 정밀도를 유지할 수 있는지 가늠할 수 있다.

예제: 무의미한 반복과 머신 엡실론

단순 반복(iteration) 알고리즘에서, $x_{k+1} = f(x_k)$ 형태의 점화식을 수치적으로 구현했다고 하자. 실제 계산에서는

x~k+1=f(x~k)+δk\tilde{x}_{k+1} = f(\tilde{x}_k) + \delta_k

라는 형태가 된다. 여기서 $\delta_k$는 각 단계에서 발생하는 반올림 오차 등을 의미한다. 머신 엡실론의 관점에서는, $\delta_k$가 누적되어 $\tilde{x}_k$를 점차 왜곡할 가능성을 염두에 두어야 한다. 특히 $x^*$가 그 점화식의 고정점(fixed point)이라고 할 때,

x=f(x)x^* = f(x^*)

이며, 실제 계산에서는

x~=f(x~)+δ\tilde{x}^* = f(\tilde{x}^*) + \delta^*

로 수렴하게 된다. 수치적 수렴점 $\tilde{x}^$와 이론적 수렴점 $x^$ 사이에 발생하는 오차가 머신 엡실론 크기의 분해능 한계, 서브노멀 여부, 그리고 반복 횟수에 따른 누적 반올림 효과 등에 따라 달라진다. 만약 $x^*$가 매우 작은 값이라면, 서브노멀 영역에 빠져서 기대보다 큰 상대 오차가 나타날 수 있으므로 추가적 주의가 필요하다.

고차원 문제에서의 오류 누적

머신 엡실론이 특히 민감하게 작용하는 상황 중 하나는 고차원 문제에서의 수치연산이다. 예컨대 대규모 선형시스템 $\mathbf{A}\mathbf{x} = \mathbf{b}$를 푸는 과정에서 수천, 수만 번 이상의 연산이 누적될 수 있다. 이 과정에서 일어나는 반올림 오류 각각은 머신 엡실론 정도일 수 있으나, 누적 효과와 연산 순서에 따라 최종 오차가 가시적으로 커질 수 있다.

직접법(direct methods)과 간접법(iterative methods)에서도 상황은 다르다. 예를 들어, 가우스 소거법(Gaussian elimination)의 전진 소거(forward elimination)와 후진 대입(back substitution)을 거치며, 서로 다른 크기의 피벗(pivot)을 다루게 될 때 반올림 오차가 부정적인 영향을 줄 수 있다. 사전 스케일링(pre-scaling)이나 행렬 재배열(피벗 전략)을 통해 이런 오차를 완화하려 해도, 결국 머신 엡실론의 한계 내에서 반올림 오류가 발생한다는 점은 동일하다.

고차원 문제를 다룰 때, 어떤 알고리즘은 작은 수를 여러 번 더하거나 빼는 과정을 포함하기도 한다. 이 경우, 축적 오차(accumulated error)나 대수적 취소가 겹쳐서 나타나면 결과가 크게 왜곡될 수 있다. 가령, 큰 합계에 매우 작은 값을 반복적으로 더해야 할 때, 그 작은 값이 머신 엡실론 이하로 감지되지 않아 합계가 더 이상 변하지 않을 수도 있다. 이런 현상을 방지하기 위해서는 커다란 데이터 집합을 처리할 때 Kahan 보정 기법(Kahan summation algorithm) 같은 수치적 안정화 기법을 적용하기도 한다.

Kahan Summation Algorithm 개념

Kahan summation은 대형 배열을 모두 더하는 과정에서 발생하는 반올림 오류를 완화하기 위한 대표적 기법이다. 단순 합산을 하면, 매우 작은 수가 큰 누적 합계에 더해질 때 반올림으로 인해 반영되지 않을 수 있다. Kahan 알고리즘은 오차를 별도로 추적해 보정하는 방식으로, 머신 엡실론 수준의 작은 항들도 가능한 한 정확히 반영되도록 유도한다. 기본 아이디어를 보면,

sumsum+xi\text{sum} \leftarrow \text{sum} + x_i

를 하는 대신, 잔여 오차를 추적하는 변수를 추가해 연산 순서를 바꾸고, 잘려나간 부동소수점 값을 적절히 반영한다. 이 같은 기법들은 전부 머신 엡실론 단위에서 일어나는 반올림을 세심하게 다루어, 결과의 유효숫자를 최대한 확보하기 위한 시도라 볼 수 있다.

사례: 다항식 평가 알고리즘과 머신 엡실론

다항식 $p(x) = a_n x^n + a_{n-1} x^{n-1} + \dots + a_1 x + a_0$를 직접 계산할 때, 단순히 항을 나열하여 $a_i x^i$를 모두 구한 뒤 더하는 방법을 생각해볼 수 있다. 그러나 이 방식은 큰 $n$에서 오차가 누적되기 쉽고, 불필요하게 많은 곱셈이 요구된다. 실제 수치해석 알고리즘에서는 호너(Horner)의 방법을 사용해 다항식 평가를 수행하는 경우가 많다. 호너의 방법은 다음과 같이 다항식을 재구성한다.

p(x)=anxn+an1xn1++a1x+a0p(x) = a_n x^n + a_{n-1} x^{n-1} + \dots + a_1 x + a_0

p(x)=anxn+an1xn1++a1x+a0=(((anx+an1)x+an2)x+)x+a0p(x) = a_n x^n + a_{n-1} x^{n-1} + \dots + a_1 x + a_0 = (((a_n x + a_{n-1}) x + a_{n-2}) x + \dots ) x + a_0

와 같이 표현한 뒤, 왼쪽부터 차례대로 연산을 수행한다. 이를 구현하면 다음과 같은 pseudo-code 형태가 된다.

이 알고리즘은 곱셈과 덧셈의 개수를 최소화하여 연산량을 줄임과 동시에, 대수적 취소나 불필요한 스케일링을 완화한다. 특히 $x$가 1보다 훨씬 큰 값이거나 1보다 훨씬 작은 값일 때, 각 항이 지나치게 커지거나 작아져서 반올림 오차가 심해지는 문제를 줄이는 효과가 있다. 호너법은 수많은 실제 수치 응용 분야에서 광범위하게 채택된다.

여기서 머신 엡실론이 의미 있게 작동하는 지점은, 항을 계산하는 과정에서 발생할 수 있는 반올림 오차가 어디에서, 어떤 방식으로 일어나는지를 체계적으로 파악함으로써, 각 단계별 오차를 누적하지 않고 상대적으로 안전한 방식을 채택하기 때문이다. 다항식 평가 중에 나타날 수 있는 대수적 취소나 작은 항의 무시 문제를 크게 완화할 수 있다는 점이 호너법의 중요한 장점이다.

예제: C++에서 Kahan Summation 알고리즘 구현

Kahan Summation 알고리즘을 C++로 간단히 구현해보면 다음과 같다. 가령 길이가 N인 배열 data에 들어 있는 모든 실수를 더하고 싶다고 하자.

이 코드는 단순 합산과 카한 알고리즘을 비교하기 위한 예시다. 작은 항을 반복해서 더할 때, 단순 합산(naiveSum)은 머신 엡실론보다 작은 차이가 잘려나가 누적되지 않을 수 있다. 반면 Kahan Summation을 활용하면 보정값 $c$를 통해 손실되려던 부분을 다시 되살려 다음 연산에 반영한다. 이런 방식은 반올림 오차가 큰 누적합에 비해 상대적으로 작은 항을 여러 번 더할 때 유용하다.

카한 알고리즘 또한 무한정 모든 오류를 해결해주는 것은 아니나, 부동소수점 체계에서 가능한 한 적은 반올림 손실을 보장하는 검증된 기법이다. 머신 엡실론 수준에서 발생하는 미시적인 오차라도, 배열의 크기가 매우 크거나 더해야 하는 항이 극단적으로 다른 크기를 가질 때에는 오차누적 문제가 심각해질 수 있으므로, 이런 기법의 도입이 필수적일 때가 많다.

Rounding to Nearest vs. 다른 반올림 모드

IEEE 754 표준에서는 일반적으로 “가장 가까운 수로 반올림하되, 정확히 중간이면 짝수 쪽으로”라는 round to nearest, ties to even 모드를 권장한다. 하지만 상황에 따라서는 round up(올림), round down(버림), toward zero(0 쪽으로) 등의 반올림 모드를 사용하는 경우도 있다. 예컨대 금융계산에서 특정 방향으로 항상 반올림해야 할 필요가 있을 수 있고, 일부 정밀도 분석에서는 고의로 round up 혹은 round down을 설정해 최악의 경우를 검사하기도 한다.

반올림 모드가 달라지면 “어떤 수와 어떤 수의 덧셈 결과가 얼마로 정규화될 것인지”가 변할 수 있기 때문에, 머신 엡실론의 역할에도 직접 간접으로 영향을 준다. 다만, “1에서 $2^{-p+1}$를 더해 구분 가능한가”라는 머신 엡실론 정의 자체는 반올림 모드가 달라져도 크게 바뀌지는 않는다. 그래서 알고리즘 분석이나 ISO C99, C++ 표준 라이브러리의 수학함수를 살펴보면, 기본 반올림 모드(가장 가까운 짝수) 전제하에 오차 경계를 제시하는 경우가 일반적이다.

Forward Error와 Backward Error 관점

수치해석에서 가장 중요한 오차 해석 기법 중 하나가 바로 전진오차(Forward Error)와 후진오차(Backward Error) 분석이다. $f$가 어떤 문제의 수학적 해라고 할 때, 실제 부동소수점 계산이 $f^*$를 내놓았을 때, 두 값 사이의 차이를 직접 비교하는 것이 전진오차이며,

ff\| f - f^* \|

를 보면 된다. 반면 후진오차는, “만약 부동소수점으로 근사 계산한 결과 $f^*$가 정확한 해라고 치자면, 원래 문제를 얼마나 변경해야 하는가”를 묻는다. 즉,

min{Δf는 (문제+Δ)의정확한해}\min \Bigl\{ \Delta \mid f^* \text{는 } (문제 + \Delta)의 정확한 해 \Bigr\}

에 해당하는 지표가 후진오차다. 많은 수치해석 알고리즘 설계 시, 후진오차가 작도록 구현하는 것이 목표다. 왜냐하면 후진오차가 작다는 것은 근본적으로 “약간만 바꾼 문제”를 완벽하게 풀었다는 의미이므로, 적어도 수치해석적으로 일관성을 확보했다고 해석할 수 있기 때문이다.

이런 관점에서 머신 엡실론은, “얼마만큼 문제를 바꾸어야 할지”를 결정할 때 하나의 스케일링 지표로 작용한다. 예컨대, 부동소수점 연산 한 번 당 최대 $\gamma = k \varepsilon$ 정도의 오차가 붙을 것이라고 가정하면, $n$회 연산 후에는 대략 $n \gamma$ 정도의 누적오차가 발생할 수 있음을 추정할 수 있다. 이는 단순 오차합산 모델일 수 있지만, 최소한의 상한선으로 알고리즘 개발에 의미 있는 참고 자료가 된다.

연산 순서와 결합법칙의 붕괴

실수해석에서는 $(a + b) + c = a + (b + c)$가 항상 참이며, 결합법칙이 문제가 되지 않는다. 그러나 부동소수점 연산에서는 결합법칙이 엄밀히 성립하지 않을 수 있다. 예컨대 다음 상황을 살펴보자.

(a+b)+ca+(b+c)(a + b) + c \quad \neq \quad a + (b + c)

이는 $a + b$ 혹은 $b + c$를 먼저 계산할 때 반올림이 다르게 일어나기 때문이다. 결합법칙이 깨진다는 점이 특히 중요한 이유는, 고수준 언어에서 최적화를 수행할 때 컴파일러가 결합법칙을 가정하고 코드를 재배치할 수 있다는 점이다. IEEE 754 표준은 결합법칙을 보장하지 않으며, C++ 표준 역시 부동소수점 연산에 대해 결합법칙이 깨질 수 있음을 명시한다. 따라서 알고리즘 설계나 코드를 작성할 때, 연산 순서가 바뀌면 결과가 달라질 수 있다는 사실을 인지해야 하며, 이를 통제하기 위한 “-ffp-contract=off”와 같은 컴파일러 옵션을 적용하는 경우도 있다.

머신 엡실론의 관점에서 보면, 결합법칙 붕괴는 결국 “어느 단계에서 발생한 반올림이 이후 단계에 전파되는가”의 문제로 이어진다. 작은 항을 먼저 더할지 큰 항을 먼저 더할지, 뺄셈을 어떤 순서로 할지 등에 따라서, 머신 엡실론 크기만큼의 오차가 누적되는 경로가 달라지기 때문이다. 수치적으로 안정한 알고리즘은 이런 결합법칙 붕괴에도 불구하고 오차가 크게 확대되지 않는 전략을 내장하고 있으며, 불안정한 알고리즘은 반올림 순서에 따라 결과가 크게 달라지는 특징을 가진다.

병렬 연산과 머신 엡실론

병렬 컴퓨팅 환경이 점차 표준화되면서, 부동소수점 연산이 여러 코어나 GPU에서 동시에 이루어지는 상황이 빈번해졌다. 이때 연산 순서가 동적으로 달라질 수 있다. 예컨대 벡터의 합을 동시에 계산할 때, 어떤 프로세싱 유닛에서 먼저 더한 값과 다른 프로세싱 유닛에서 나중에 더한 값이 뒤섞여 최종 결과가 합쳐지는 식이다. 직렬 계산이라면 일관된 순서대로 연산이 이루어지지만, 병렬 계산에서는 결합법칙이 깨지므로 동일한 입력이라도 다른 합계가 산출될 수 있다.

이런 문제를 “재현성(reproducibility) 이슈”라고 부르기도 한다. 고성능 계산(HPC) 분야에서 대규모 행렬 연산이나 벡터 합을 수행할 때, 같은 코드를 동일한 기계에서 돌려도 매번 다소 다른 결과가 나올 수 있는데, 이는 병렬 스케줄링 혹은 쓰레드 동기화 방식에 따라 연산 순서가 가변적으로 결정되기 때문이다. 머신 엡실론 단위의 반올림 오차가 서로 다른 타이밍으로 축적되므로, 오차 양상이 변해 최종 합계가 약간씩 달라지는 것이다.

병렬 연산에서 머신 엡실론 관련 오차를 제어하려면, 어떤 방식으로 합산을 계층화할지(트리 형태로 부분합을 모은 후 최종 합산), 혹은 Kahan Summation과 같은 기법을 병렬화하기 위한 대안을 모색할지가 중요 과제로 떠오른다. 부분합을 큰 값을 기준으로 정렬한 뒤 더하거나, FFT 기반 합산 기법을 사용하는 등 다양한 시도가 있을 수 있으나, 완전한 결합법칙 보장은 불가능하다는 점을 기억해야 한다.

혼합 정밀도(Mixed Precision) 기법

최근 머신러닝과 같은 대규모 계산 환경에서, 혼합 정밀도(mixed precision)가 각광받고 있다. 예컨대 연산 자체는 단정도(single precision)나 bfloat16과 같은 저정밀도를 사용하되, 축적(accumulation) 과정이나 일부 핵심 구간에서는 배정도(double precision)를 활용하는 식이다. GPU에서 혼합 정밀도를 사용하면 메모리 사용량과 연산 시간을 크게 줄일 수 있으면서도, 일정 수준의 정확도는 유지할 수 있다는 장점이 있다.

혼합 정밀도 알고리즘에서 머신 엡실론은 이중으로 작동한다. 단정도 수준의 머신 엡실론은 $2^{-23}$이며, 배정도 수준은 $2^{-52}$다. 따라서 미세한 항이나 오차 보정을 처리해야 할 때 배정도를 사용해 누락될 수 있는 수를 최소화하고, 대규모 반복 연산에서는 단정도를 사용해 성능을 극대화한다. 중요한 것은, 어떤 구간에서 어느 정밀도를 사용해야 전체 알고리즘의 결과가 안정적으로 유지되는지 판단하는 기준을 세우는 일이다.

혼합 정밀도 방법은 특히 기계학습(예: 딥러닝)에서 주어진 신경망 모델의 가중치를 빠르게 갱신하는 과정에 많이 쓰인다. 부동소수점 오차가 누적되어도, 통계적 옵티마를 찾는 과정이기에 어느 정도 범용적인 정밀도 허용을 해도 학습이 잘 이루어지는 경우가 많다. 반면, 고전적인 선형대수 해석(예: 정밀한 선형 시스템 풀이)에서는 여전히 배정도 이상의 정밀도가 필요한 상황이 있을 수 있다.

bfloat16과 머신 엡실론

bfloat16은 구글 TPU 등을 통해 대두된 새로운 형식으로, 부호 1비트, 지수 8비트, 가수 7비트의 구조를 가진다. IEEE 754의 반정도(half precision)인 float16과 달리, 지수부가 큰 대신 가수부 정밀도는 훨씬 낮다. 이는 매우 큰 혹은 매우 작은 값의 범위를 넓게 커버하기 위해 고안된 스펙이다. 하지만 가수부가 7비트에 불과하므로, 머신 엡실론은 $2^{-7}$ 정도가 된다.

bfloat16은 신경망 학습에서 아주 유용하다. 학습 과정에서 가중치가 급격히 커지거나 작아질 때에도 표현 범위가 넓기 때문에 underflow, overflow 문제를 완화한다. 그러나 가수부 정밀도가 낮으므로, 매우 근접한 두 수를 구분하는 능력은 떨어진다. 머신 엡실론이 배정도에 비해 상당히 커서, 작은 변동이 반올림에 의해 사라질 확률이 높다. 따라서 bfloat16을 사용할 때는 정밀도 손실을 최소화하기 위해 축적은 배정도(혹은 적어도 단정도)로 수행하고, 다시 bfloat16으로 변환하는 식으로 혼합 정밀도 기법을 적용할 수 있다.

머신 엡실론과 유리수 근사

부동소수점 체계는 어떤 형태로든 유한 비트로 수를 표현하므로, 실제로는 모든 실수를 근사하는 대신 특정 유리수 집합(분모가 $2^p$ 형태인 분수)만 표현한다. 예컨대 0.1(10진법)은 이진 부동소수점으로 정확히 표현되지 않는다. $0.1_{10} \approx 0.0\overline{0011}_{2}$로 끝없이 반복되기 때문에, 내부적으로는 근사값만 저장된다. 이때, $0.1$과 유사한 값 $0.10000000149011612\dots$ 등이 실제 메모리에 배정되는 실수 값일 수 있다.

이는 머신 엡실론과 직접적으로 연관된다. $0.1$ 같은 십진수로 표현된 상수를 입력하면, 부동소수점 변환 과정에서 특정 이진수 근사로 반올림된다. 이후 연산을 거치며 이러한 근사값이 다시 반올림되고, 그 결과가 조금씩 달라져서 예측하기 까다로운 현상이 발생한다. 이런 현상을 종종 “십진 부동소수점 오차”라고 부르는데, 본질적으로는 부동소수점 정밀도의 한계와 머신 엡실론의 존재가 결합된 결과라고 볼 수 있다.

무리수와 초월수의 표현 한계

$\pi$나 $e$처럼 소수점 아래가 무한히 이어지는 무리수 역시 부동소수점 표현에선 유한 비트로 잘려서 근사될 수밖에 없다. 이 근사는 기계 내부에서 반복 사용될 때, 누적된 반올림 오차가 조금씩 증가하여 원래 이론적 값과는 달라진 결과를 낳는다. 디지털 계산 환경에서 무리수, 초월수를 다룰 때에는 수치 라이브러리들이 제공하는 고정밀도(다중 정밀도) 함수를 활용하거나, 적절한 근사 오차 범위를 계산하여 허용할 수 있는지를 점검해야 한다.

머신 엡실론이 알려주는 것은 “1을 기준으로 한 가장 작은 간격”이지만, 수치 계산 시에는 $\pi$나 $e$의 근사값이 엄밀하지 못함으로 인해 추가 오차가 발생할 수 있다는 점을 늘 유념해야 한다. 예컨대 “$\pi$라는 상수를 매번 사용하는” 상황이라도, 내부에서 $3.141592653589793\ldots$가 대략적 근사치로 저장되어 사용되는 것이다. 작은 소수점 차이가 반복 연산 과정에서 누적되면, 나중에 상당한 차이가 될 수도 있다.

수치 미분과 머신 엡실론

수치 미분(Numerical differentiation)은 $f'(x)$를 근사하기 위해

f(x)f(x+h)f(x)hf'(x) \approx \frac{f(x + h) - f(x)}{h}

와 같은 차분 스킴을 활용한다. 여기서 $h$가 너무 크면 근사오차가 커지고, 반대로 너무 작으면 부동소수점 반올림 오차가 두드러지게 된다. $h$가 너무 작을 때 문제되는 반올림 오차의 대표적 원인이 바로 머신 엡실론이다. $f(x + h)$와 $f(x)$가 매우 근접한 값이 되면, 뺄셈으로 인해 대수적 취소가 발생하고 상대 오차가 크게 증폭된다.

적절한 $h$의 선택은 $O(h^2)$ 수준의 이산화 오차(truncation error)와 $O(\frac{1}{h})$ 수준의 반올림 오차(rounding error) 간의 균형을 맞추는 문제로 귀결된다. 이 균형점이 수치해석 교과서에서 흔히 소개되는 “$\sqrt{\varepsilon}$ 수준의 $h$를 고르면 된다”는 정석으로 이어진다. 여기서 $\varepsilon$는 머신 엡실론을 의미한다. 구체적으로는 $h \approx \sqrt{\varepsilon} \cdot x$와 같은 스케일로 잡을 때, 근사적 최적점에 근접한다고 분석한다.

수치 적분과 머신 엡실론

수치 적분에서도 반올림 오차가 고려 대상이다. 예를 들어, 구간을 여러 개로 쪼개 적분값을 합산할 때, 각 부분구간에서 생기는 작은 적분값이 큰 누적합과 더해지며 머핀(muffin)처럼 오차가 사라질 우려가 있다. Simpson, Trapezoidal, Gaussian Quadrature 등 다양한 방법이 있으나, 기본적으로 “합산” 과정에서 발생할 수 있는 반올림 문제를 어떠한 방식으로 완화할지 고민해야 한다.

Kahan Summation 기법을 적용하거나, 큰 구간과 작은 구간을 분리하여 따로 계산 후 합치는 방식 등이 동원되기도 한다. 다만 적분 문제는 일반적으로 비교적 ‘매끈한’ 함수에 대해 구간 분할을 많이 하기 때문에, 수치 미분보다 대수적 취소가 극적으로 발생하는 경우는 덜하다. 대신 구간이 지나치게 많아질수록 연산 횟수가 증가하므로, 머신 엡실론 수준의 오차가 누적될 가능성은 커진다.

초정밀(Arbitrary Precision) 연산과 머신 엡실론

머신 엡실론은 “현재 사용하는 부동소수점 표현”에 종속적인 값이다. 임의정밀도(arbitrary precision) 라이브러리를 활용하면, 사용자가 원하는 만큼 많은 비트로 실수를 표현할 수 있다. 예컨대 128비트, 256비트, 심지어 1000비트 이상의 정밀도로 연산한다면, 그만큼 작은 머신 엡실론을 설정할 수 있다.

그러나 임의정밀도 연산은 계산 비용이 급증한다. $p$비트 정밀도로 곱셈을 하면, 일반적인 CPU 내장 곱셈보다 훨씬 많은 연산 단계를 거쳐야 한다. 따라서 모든 문제를 고정밀도 방식으로 해결할 수는 없으며, 문제의 요구 사항에 따라 어디까지 정밀도를 올릴 것인지 결정해야 한다. 고정밀도 연산을 사용해도 대수적 취소 문제 자체가 완전히 사라지지는 않으므로, 수치 안정성이 필요한 상황에서는 알고리즘적 설계와 정밀도의 균형을 맞추는 전략이 중요하다.

정수 연산과의 관계

정수 연산에서는 머신 엡실론 개념이 크게 부각되지 않는다. 정수 연산은 정확도가 보장되며, $a + b = c$가 항상 정확한 정수 결과로 표현된다(오버플로우가 일어나지 않는 한). 그러나 부동소수점으로 변환되는 순간, 예컨대 $a$와 $b$를 배정도 실수로 바꿔서 연산하면 반올림 현상이 나타난다.

정수에서 부동소수점으로 전환할 때, 혹은 그 반대 과정을 수행할 때, “정확히 표현 가능한 범위”와 “근사가 필요한 범위”가 명확히 구분된다. 64비트 정수는 $2^{53}$ 이상의 구간에서 배정도 부동소수점의 정밀도와 충돌이 시작된다. 왜냐하면 배정도에서 53비트의 정밀도만 유지할 수 있기 때문이다. 예컨대 64비트 정수로 표현되는 아주 큰 값은 부동소수점에서 “틈”이 벌어져 연속성이 깨진다. 이는 머신 엡실론이 $2^{-52}$ 수준인 배정도와 직접적으로 연결되는 이슈다.

머신 엡실론을 시각화하기 위한 mermaid 예시

머신 엡실론의 누적 과정을 간략히 나타내면 다음과 같은 의사 다이어그램을 구상할 수 있다.

spinner

이 다이어그램은 $x$라는 실제 실수를 컴퓨터가 부동소수점으로 표현하는 과정을 나타낸다. 반올림으로 인해 $x$가 $\tilde{x}$로 치환되고, 이후 연산 결과가 다시 반올림 과정을 거친다. 각 단계에서 $O(\varepsilon)$ 수준의 반올림이 누적되어, 최종 결과와 이론적 이상치 사이에 차이가 발생한다는 것을 보여준다.

언어별 머신 엡실론 상수

일부 언어와 라이브러리 환경에서는 머신 엡실론을 상수로 미리 정의해둔다. 예를 들어, C나 C++에서는 헤더 파일 에 DBL_EPSILON, FLT_EPSILON 등의 매크로가 있다. 여기서 DBL_EPSILON은 배정도(double precision)에서 1과 1이 아닌 수를 판별하는 최소 간격이며, FLT_EPSILON은 단정도(single precision)에 해당한다. 예컨대 C++에서 다음과 같은 코드를 작성해 확인할 수 있다.

이 코드를 빌드해 실행하면, $\texttt{DBL_EPSILON} \approx 2.2204460492503131 \times 10^{-16}$, $FLT_EPSILON \approx 1.1920928955078125 \times 10^{-7}$ 정도가 출력된다. 이는 각각 $2^{-52}$와 $2^{-23}$ 정도의 값을 10진수로 변환한 결과다. 실제로 C++ 표준 라이브러리 문서에는 “1과 구분할 수 있는 배정도 부동소수점 수 중 최소의 차”라고 명시되어 있으며, 소스 코드로 확인한 값과 같은 정의를 따른다.

언어마다 표준적으로 제공되는 상수 이름이나 형식은 약간씩 다를 수 있다. 예를 들어, Fortran에서는 EPSILON() 함수를 통해 타입별 머신 엡실론을 얻을 수 있고, Python에서는 sys.float_info.epsilon 속성을 통해 배정도 머신 엡실론을 확인할 수 있다. 그러나 IEEE 754가 보편화된 현재 환경에서는 대부분 동일하거나 유사한 값을 갖는다.

고유 정밀도별 머신 엡실론

IEEE 754 표준은 배정도(double precision)와 단정도(single precision)를 필수로 규정하고 있으며, 그 외에도 확장 정밀도, 반정도(half precision) 등이 추가로 존재한다. 반정도(float16)는 5비트의 지수부와 10비트(숨겨진 비트 포함) 미만의 가수부를 가지므로, 이 경우 머신 엡실론은 $2^{-10}$ 정도가 된다. 다만 CPU나 GPU 내부에서 반정도 연산을 직접 지원하는지는 하드웨어마다 차이가 있다.

내부적으로 128비트 이상의 쌍배정도(quad precision)나 그보다 높은 정밀도를 소프트웨어 라이브러리로 제공하는 환경도 있다. 이 경우 머신 엡실론은 $2^{-113}$ 정도로 훨씬 더 작아지며, 매우 세밀한 수치 계산이 가능해진다. 다만, 그러한 고정밀도 연산은 속도와 메모리 사용량에서 큰 희생을 감수해야 하므로, 실무에서는 필요에 따라 부분적으로만 활용하는 경우가 많다.

컴파일러 최적화와 정확도 이슈

최적화 옵션을 적용하면, 컴파일러가 부동소수점 연산 순서를 재배치하거나, 특정 연산을 상수 취급해 정적 평가(constant folding)로 처리하는 등 여러 가지 “유리해 보이는” 변환을 적용할 수 있다. 이때 결합법칙이 엄격히 지켜지지 않는 부동소수점 연산에서는, 머신 엡실론 수준의 미묘한 반올림 차이가 연산 순서 변경과 맞물려 최종 결과를 변화시킬 수 있다.

예를 들어, 어떤 최적화가 $(a + b) + c$를 $a + (b + c)$로 바꿀 수 있다. 실제로 $b + c$가 극도로 작을 경우, $a + b$를 먼저 계산하는지에 따라 반올림 경계가 달라질 수 있다. 알고리즘이 수치적으로 충분히 안정하면 이 정도 차이는 크게 문제되지 않지만, 민감한 계산에선 결과가 달라져 예측하기 어려운 오차가 생길 수 있다. 이러한 이유로, 특정 응용에서는 -ffp-contract=off, -fno-fast-math, -frounding-math 같은 옵션을 적용해 부동소수점 연산 최적화를 제한하는 전략을 쓰기도 한다.

머신 엡실론과 부동소수점 비교 연산

프로그래밍에서 부동소수점을 직접 비교할 때 $a == b$처럼 “정확히 동일”을 요구하면, 머신 엡실론 단위의 오차 때문에 원치 않는 결과가 나올 수 있다. 예컨대 $0.1 + 0.2$를 계산해놓고, 그 결과가 $0.3$과 같은지를 검사하면, 내부 근사로 인해 false가 나올 수 있다. 실제로 $0.1 + 0.2 = 0.30000000000000004\dots$와 비슷한 수가 저장되기 때문이다.

이런 맥락에서, “부동소수점 수 두 개가 충분히 가깝다”는 것을 판단하기 위해 $\varepsilon$을 활용하는 방법이 흔히 쓰인다. 예를 들어, 다음 조건을 만족하면 두 수가 사실상 같다고 볼 수 있다는 식이다.

abτmax(1,a,b)|a - b| \le \tau \max(1, |a|, |b|)

여기서 $\tau$는 머신 엡실론이나 그 배수 정도로 설정한다. 이 방식을 “상대/절대 혼합 오차”라고 부른다. 조건을 약간 더 세분화해 상대 오차만 보거나 절대 오차만 보기도 하지만, 대부분은 데이터 스케일에 맞춰 $\tau$값을 조절한다. 특히 매우 큰 수나 매우 작은 수를 비교할 때는 절대 오차와 상대 오차 기준이 달라질 수 있으므로, 수치 해석 설계자가 적절한 비교 공식을 선택해야 한다.

정규화 과정과 Rounding 단계 요약

부동소수점 연산을 정리해보면, 내부에서 대략 다음 단계를 거친다:

정규화가수부 정밀도 맞춤반올림결과 저장\text{정규화} \rightarrow \text{가수부 정밀도 맞춤} \rightarrow \text{반올림} \rightarrow \text{결과 저장}

1에서 숨겨진 비트(leading 1)를 고려해 소수점 위치를 조절한 후, 필요한 만큼 버림, 올림, 근접수 반올림을 적용한다. 머신 엡실론은 이 전체 단계에서 1과 1이 아닌 수의 분해 가능한 최소 단계를 가늠하는 척도다. 결국, 머신 엡실론은 수치해석 알고리즘이 “이 연산이 어느 시점에서 더 이상 구분이 안 될 것인가”를 파악할 때, 가장 기초가 되는 기준값이라 할 수 있다.

다른 수치 연산 라이브러리에서의 에러 경계

대다수 수학 라이브러리나 블라스(BLAS), 라팩(LAPACK) 같은 선형대수 라이브러리는 내부적으로 머신 엡실론 크기를 바탕으로 오차 경계를 설정하거나, 특정 알고리즘 분기를 결정한다. 예를 들어, 어떤 루틴이 “피벗이 너무 작다”고 판단할 때 “이 수는 0으로 처리하겠다”고 결정할 수 있는데, 그 기준이 머신 엡실론 배수나 서브노멀 영역에 대한 판단일 수 있다. 수치 라이브러리를 사용할 때 이 부분을 세부적으로 제어하는 경우는 드물지만, 라이브러리 설계 관점에서 머신 엡실론은 핵심 지표다.

파생 개념: Double-Double, Quad-Double 정밀도

배정도(64비트)보다 더 높은 정밀도를 요구하지만, 완전히 소프트웨어 기반의 임의정밀도(arbitrary precision) 연산은 너무 무겁게 느껴지는 경우, Double-Double(이중 배정도) 혹은 Quad-Double(4중 배정도) 같은 구현이 중간 해법이 될 수 있다. 이는 간단히 말해, 배정도 실수를 두 개(또는 네 개) 이어붙여 하나의 숫자를 표현하는 방식이다. 예컨대 Double-Double 포맷은 배정도 실수 두 개를 합쳐, 이론적으로 약 106비트(53×2)의 유효숫자를 확보한다.

실제로는 한 쌍의 배정도 실수를 $(A, B)$로 두어, $A$가 “주요 값”을 담고, $B$가 “잔차”를 담는다. 예를 들어,

xA+Bx \approx A + B

로 해석한다. 여기서 $A$는 표준 배정도 범위에서 표현되는 부동소수점이며, $B$는 $A$보다 여러 배 작은 보정 항이다. 이렇게 하면, $A + B$를 통해 더 높은 정밀도를 흉내 낼 수 있다. 연산마다 $B$ 항목까지 반영해 반올림 오차를 가능한 한 줄이고, 일반 배정도 연산보다는 조금 더 많은 계산 단계를 거치지만, 완전히 임의정밀도 라이브러리를 쓰는 것보다는 훨씬 빠르다.

Double-Double, Quad-Double 방식에서도 “머신 엡실론”에 대응하는 새로운 분해능이 형성된다. 예컨대 Double-Double 연산에서의 머신 엡실론은 기본 배정도의 머신 엡실론보다 훨씬 작다. 소프트웨어 라이브러리로 구현할 때는, 내부에서 덧셈이나 곱셈을 여러 단계로 나누어 수행하고, 발생하는 반올림 오차를 추가 항에 저장한다. 이 방식은 다항식 평가, 선형시스템 풀이 등에서 한층 높은 정확도를 요구할 때 사용된다.

임의정밀도와 성능 고려

수치해석 이론에서야 정밀도가 높을수록 좋다. 하지만 실제 계산 성능을 감안하면, 머신 엡실론을 줄이기 위해 임의정밀도 수단을 사용하는 것이 항상 바람직한 것은 아니다. $n$비트 정밀도로 곱셈을 하면, 복잡도가 $\mathcal{O}(n^2)$에 달하는 전통적 알고리즘부터 FFT 기반 곱셈 기법 등이 고려되어야 하고, 메모리 접근량도 증가한다. 따라서 대규모 행렬 연산이나 대수 방정식을 풀 때, 필요 이상의 높은 정밀도로 무조건 계산하면 오히려 계산 시간이 기하급수적으로 늘어날 수 있다.

실무에서 혼합정밀도 기법, Double-Double 기법 등이 애용되는 이유는, 배정도의 머신 엡실론($2^{-52}$)을 넘어서는 정밀도가 필요할 때마다 임의정밀도 라이브러리에 의존하는 대신, 상대적으로 더 빠른 중간 수단을 쓰려는 전략적 판단 때문이다. 이는 곧 “내 문제에서 어느 정도 오차까지 허용 가능한가”라는 사용자의 사전 지식과, “해당 정밀도의 연산 속도와 구현 난이도”를 맞추는 최적화 과정이라 할 수 있다.

정리: 머신 엡실론의 광범위한 활용

머신 엡실론은 단지 “1과 1이 아닌 수를 구분할 수 있는 최소 간격”을 넘어, 부동소수점 연산 정밀도 전반을 이해하는 핵심 개념으로 자리 잡아 있다. 반올림 오차, 대수적 취소, 연산 순서의 비결합성, 병렬 환경에서의 재현성 문제, 혼합정밀도 전략까지 폭넓은 영역에서, 머신 엡실론을 지표로 삼아 오차를 평가하고 수치 알고리즘의 안정성을 설계한다.

진정한 의미에서 완벽한 정밀도 계산은 이산적인 디지털 환경에서는 불가능에 가깝기 때문에, 우리는 “가용한 자원과 시간 안에서 어떤 정밀도를 확보할 것인가”라는 현실적인 문제에 끊임없이 맞닥뜨린다. 이때 필연적으로 등장하는 개념이 머신 엡실론이므로, 수치해석에 입문하는 사람이라면 반드시 숙지해야 한다.

Last updated