기술 문서

Delphi에서의 Excel 통계 함수 구현: NORM, CHISQ, BETA

셀에 =NORM.DIST(115,100,15,TRUE)를 입력하면 Excel은 부가 설명 없이 즉각 0.8413447을 반환합니다. 이 호출은 간단한 값 조회처럼 보이지만 그렇지 않습니다. 그 하나의 숫자 배후에는 대수적으로 풀리지 않는(Closed form이 없는) 누적 정규 분포 적분 연산이 자리 잡고 있으며, CHISQ.INV.RTBETA.DIST 배후에는 단순히 손으로 근사치를 구해서는 안 되고 정밀 수치 해석 라이브러리가 명확하게 평가해야 하는 특수 함수들이 존재합니다. Excel 호환성을 주장하는 스프레드시트 컴포넌트는 단지 함수명만 흉내 내는 것이 아니라 수치 해석 방법까지 정밀하게 구현하여 Excel이 출력하는 마지막 자릿수까지 동일하게 재현해 내야 합니다

HotXLS는 50개 이상의 이러한 통계 함수들을 구현하였으며, 연산의 정확성을 기하는 정교한 작업은 수식 입력줄 뒤에 가려 보이지 않습니다. 이 가이드는 엔진이 통계 함수들을 어떻게 처리하는지에 대한 여정입니다: 공유되는 특수 함수 코어, 산술 연산의 안정성을 유지하는 분기 결정, 그리고 일반적인 테스트 케이스에서는 작동 오류가 감지되지 않아 장기간 발견되지 못했던 역정규분포 꼬리 영역의 Log 함수 버그 분석을 포함합니다

한 번의 호출 뒤에 구동되는 50가지의 확률 분포들

지원되는 함수군은 통계 분석 시트에서 널리 활용되는 모든 주요 범주를 포괄합니다. NORM.DISTNORM.S.DIST와 이들의 역함수를 포함하는 정규분포군; GAMMA.DIST, CHISQ.DIST, CHISQ.DIST.RT, CHISQ.INV.RT로 구성된 감마 및 카이제곱분포군; BETA.DISTBETA.INV의 베타분포군; T.DIST, T.DIST.2T, F.DIST, F.INV 등의 표본분포군; 이산확률분포 쌍인 BINOM.DISTPOISSON.DIST; 그리고 신뢰구간 정산 도구인 CONFIDENCE.TCONFIDENCE.NORM 등이 포함됩니다. 호출 측 입장에서는 이들 각각이 단일 수식에 불과합니다. 셀에 매개변수들을 기입하고 계산을 실행하면 결과를 즉시 획득할 수 있습니다

var
  wb: IXLSWorkbook;
  sh: IXLSWorksheet;
begin
  wb := TXLSWorkbook.Create;
  sh := wb.Sheets.Add;
  sh.Range['A1', 'A1'].Value := 115;   // 관측값
  sh.Range['A2', 'A2'].Value := 100;   // 평균
  sh.Range['A3', 'A3'].Value := 15;    // 표준편차

  // XLS 수식 파서는 ';'를 인자 구분자로 사용합니다.
  Writeln(wb.Calculate('=NORM.DIST(A1;A2;A3;TRUE())'));   // 0.8413447
  Writeln(wb.Calculate('=CHISQ.INV.RT(0.05;10)'));        // 18.3070381
  Writeln(wb.Calculate('=BETA.DIST(0.5;2;3;TRUE())'));    // 0.6875
end;

모든 통계 연산의 기반이 되는 두 가지 핵심 특수 함수

이 패키지에 포함된 대부분의 누적 확률 분포(CDF)는 고유 정의에 따라 직접 합산하거나 적분하여 연산되지 않습니다. 대신 정규화된 하부 불완전 감마 함수(Regularized lower incomplete gamma, P(a, x)로 표기)와 정규화된 불완전 베타 함수(Regularized incomplete beta, Ix(a, b)로 표기)라는 두 가지 특수 함수를 통해 연산됩니다. 내부적으로 디스패처가 이 두 헬퍼 함수를 매개로 동작하므로 호출 체인은 매우 정교합니다. 카이제곱 CDF는 형태 인자가 df/2이고 척도 인자가 2인 감마 CDF와 동등합니다. 감마 CDF는 곧바로 P(a, x)를 의미합니다. t, F 및 이항 누적 분포 함수는 모두 적절한 인수를 갖는 정규화된 불완전 베타 함수 값으로 표현됩니다. 포아송 CDF는 상부 불완전 감마 Q 구조와 연계됩니다. 이 감마 및 베타 함수를 견고하게 구현해 두면 10여 개의 다른 분포들의 정밀도를 자동으로 확보할 수 있습니다

여기서 '정규화(Regularized)'라는 용어가 정밀성 보장의 핵심입니다. 정규화되지 않은 순수 불완전 감마는 계승(팩토리얼)처럼 기하급수적으로 폭증하며 순수 베타 적분은 결과 값이 유효 범위에 도달하기도 전에 쉽게 언더플로나 오버플로를 일으킵니다. 정규화된 형태는 완전 감마 또는 완전 베타로 정규 분산 나눗셈 처리를 거치므로, 확률 값의 원래 한계 영역인 0에서 1 사이의 폐구간 내에 완벽하게 안착합니다. 이러한 정규화 연산 덕분에 중간 연산 항이 배정밀도(double) 부동소수점 한계를 초과하는 크래시를 내지 않고 자유도가 2인 카이제곱분포와 자유도가 200인 카이제곱분포를 동일한 루틴으로 안정적으로 계산할 수 있습니다. 또한 누적 확률 분포를 단순 밀도 항들을 늘어놓아 무한 합산하는 방식으로 연산하지 않는 이유도 여기에 있습니다: 개별 항마다 고유의 반올림 오차가 있어 계산 횟수가 누적될수록 오차가 걷잡을 수 없이 전파되는데, 정규화된 특수 함수는 무한 급수 합산 대신 빠르게 수렴하는 급수나 연분수(Continued fraction) 평가법을 사용하여 이 오차 확산 현상을 완벽하게 피합니다

대각선 하부는 급수로, 대각선 상부는 연분수로 계산

불완전 감마 루틴은 계산을 실행하기 전에 한 가지 주요 의사결정을 수행합니다: 바로 x 값을 a + 1 수치와 비교하는 것입니다. 이 판별 경계선은 자의적으로 정한 것이 아닙니다. P(a, x)의 멱급수(Power-series) 전개식은 x가 a에 비해 작을 때 매우 신속하게 수렴하지만, x가 크면 수렴 속도가 급격히 정체되어 연산 효율이 극도로 유실됩니다. 연분수(Continued fraction) 기법은 이와 정반대의 수렴 특성을 보입니다. 따라서 엔진은 x가 a + 1 미만일 때는 멱급수 기법을 취하고, x가 a + 1 이상일 때는 렌츠(Lentz) 연분수 기법을 채택하여, 각 연산 분기가 고효율을 낼 수 있는 작업만 분담하도록 유도합니다

렌츠 연분수 연산 루틴에는 하나의 정밀 장치가 장착되어 있어야 합니다. 렌츠 공식은 주기마다 활성 분자와 분모 값을 유지하고 매 스텝마다 분모를 역수 취하는 방식으로 작동하는데, 연산 도중 두 값 중 하나라도 0에 가닿으면 역수 나눗셈 연산 오류가 발생합니다. 해결책은 매우 미세한 정밀 하한선(Floor)을 보강하는 것입니다. 계산 중 중간 수치 항의 절대 크기가 대략 1e-30 미만으로 감소하면 이를 강제로 1e-30으로 고정(Clamping)하여, 최종 수렴 값의 정밀도를 손상시키지 않으면서 재귀 점화식이 발산하는 오작동을 차단합니다. 동일한 클램핑 기법이 동일한 원인으로 불완전 베타 함수의 연분수 연산 단에도 내장되어 있습니다. 이 미세한 상수가 수치 연산의 기틀을 잡아 주어, 계산 엔진이 0과 구별하기 힘든 아주 작은 수치로 나눗셈을 하다가 터지는 것을 방지하고 산술적 안정성을 보장합니다

상부 꼬리 확률 분포 Q(a, x)는 수학적으로 단순히 1 - P(a, x)이며, 이것이 포아송 누적 분포가 작동하는 방식입니다: 평균이 λ인 분포에서 최대 k회 이벤트가 발생할 확률은 곧바로 Q(k + 1, λ)입니다. 포아송 항 k + 1개를 개별적으로 연산해 더하는 방식 대신 상부 불완전 감마 함수 단일 연산으로 경로를 단일화한 것은, 여러 개의 작은 오차 요소를 누적하는 구조를 피하고 빠르게 수렴하는 하나의 최적화된 식을 평가하기 위한 정밀한 설계 선택입니다

계승(Factorial) 오버플로 없이 이산확률질량 정산

이산확률질량함수 연산에는 조합(Binomial coefficient) 계산이 수반되는데, 예를 들어 '52개 중 26개를 고르는 조합 수(52-choose-26)'만 해도 부동소수점 double 표현 한계를 아득히 초과하는 천문학적인 정수입니다. 이를 공식 그대로 분수 구조로 선언하면 나눗셈을 통해 확률 영역인 0~1 사이 값으로 희석시키기도 전에 이미 분자 연산 단계에서 오버플로 크래시가 유발됩니다. HotXLS 엔진은 이 큰 숫자를 직접 조립하지 않습니다. 대신 로그 감마(Log-gamma) 함수를 활용하여 계승(팩토리얼) 연산 자체를 로그 공간(Log space)으로 치환해 계산하며, 로그들을 덧셈과 뺄셈으로 정산하고 성공 및 실패 확률의 로그 값을 가산한 뒤, 최종 마감 단계에서 단 한 번 지수(Exp) 연산으로 복원합니다

// 로그 공간에서 완벽하게 계산되는 이항확률질량 연산.
// LnGammaF(n+1)은 ln(n!)을 의미하며, 세 로그 계승이 ln(C(n,k)) 조합을 형성합니다.
// 단 한 번의 Exp 호출 전에 전체 지수 식이 정교하게 구성됩니다.
//   ln P(X=k) = ln(n!) - ln(k!) - ln((n-k)!) + k*ln(p) + (n-k)*ln(1-p)
result := Exp(LnGammaF(nt + 1) - LnGammaF(kk + 1) - LnGammaF(nt - kk + 1)
  + kk * Ln(pp) + (nt - kk) * Ln(1 - pp));

로그 감마 함수 자체는 양의 영역 전반에 걸쳐 신뢰도가 검증되고 계산 비용도 저렴한 란초스 근사법(Lanczos approximation)을 채택하고 있습니다. 모든 폭증할 수 있는 중간 변수를 최종 Exp가 수행되기 전까지는 로그 값으로 가두어 취급하므로, 루틴이 마주하는 최대 크기의 수치는 최종 결과 확률 값인 1 이하의 수치로 제한됩니다. 포아송 확률질량함수 역시 동일한 아키텍처를 공유하여 분모의 팩토리얼 연산을 단일 로그 감마 항으로 치환해 안전하게 계산합니다. 또한 확률 p가 정확히 0이거나 1인 경계 부분은 에러(Ln(0) 호출 방지)를 차단하기 위해 예외 처리가 마련되어 있습니다. 이에 따라 HotXLS는 BINOM.DIST(5,10,0.5,FALSE)에 대해 0.2460938을, 누적 POISSON.DIST(2,2,TRUE)에 대해 0.6766764를 완벽하게 도출하여 Excel과 정밀도를 일치시킵니다

정방향 곡선 범위를 활용한 역함수(Inverse) 수치 연산

역확률분포 함수는 그 반대의 질문을 던집니다: 주어진 누적 확률에 대해, 해당 CDF 값과 일치하게 만드는 정방향 변수 값을 찾는 연산입니다. 이 라이브러리에 정의된 역함수 중 오직 하나만이 수렴 연산이 필요 없는 직관적인 계산 수식을 갖습니다. 표준 역정규분포인 NORM.S.INV는 전체 구간을 중앙 영역과 두 꼬리 영역으로 나누어, double의 정밀도 한계 내에서 완벽하게 대칭 연산되는 한 쌍의 다항식 비율 구조인 아클람 유리수 근사식(Acklam rational approximation)을 적용합니다. 이는 순환 반복 계산을 요하지 않는 순수 대수 연산법입니다

다른 역함수들은 이러한 직접적인 폐쇄형 수식이 존재하지 않으므로, 계산 엔진은 수치적으로 이를 조율하여 계산합니다. 각 확률 분포의 지지 집합(Support)에서 도출한 상한선과 하한선으로 정답 영역을 가둔(Bracketing) 뒤, 이분법(Bisection)을 실행합니다: 그 중간 지점에서 정방향 CDF 값을 계산해 보고 타겟 확률을 포함하는 쪽으로 상/하한 경계선을 조여 가며 구간 폭이 극히 좁아질 때까지 반복 순회합니다. 감마 및 카이제곱 역함수의 경우 하한선은 0부터 출발하며 상한선은 분포의 형태와 척도 상수를 고려해 여유 있게 설정하고, 타겟 확률이 아직 경계 내에 수집되지 않은 경우 상한선을 배로 늘려갑니다. t 역함수는 외부로 확장되는 대칭 경계선을 조율하며, F 역함수는 음수가 아닌 구간에서 이분 연산을 실행합니다. 호출당 수십 회의 CDF 계산 비용이 소모되지만 현대적인 CPU 속도에서는 무시할 수 있는 수준이며, 이로 인해 모든 역함수가 자신이 역연산하는 정방향 CDF 함수와 완벽하게 대칭되는 정확도를 확보하게 됩니다. CHISQ.DIST(CHISQ.INV(0.7,5),5,TRUE)와 같은 왕복 수식 연산이 소수점 오차 없이 정확히 0.7을 반환하는 이유가 바로 이 이분법 수치 연산 덕분입니다

역정규분포 꼬리 영역의 상용로그(Base-10 log) 버그 분석

아클람 표준 역정규분포 루틴은 총 세 개의 연산 분기를 거칩니다. 입력 확률이 약 0.025와 0.975 사이에 안착할 때 적용되는 범용 중앙 분기는 로그 함수가 배제된 순수 다항식 연산 비율로 수치를 도출합니다. 극히 작거나 큰 확률 값을 처리하는 양쪽 꼬리 영역 분기는 계산 전에 입력 확률의 로그를 먼저 계산하는데, 확률 분포의 꼬리 형상이 p의 자연로그에 음수를 곱한 값의 제곱근과 비례하여 발산하기 때문입니다

초기 버전의 꼬리 분기 코드에는 자연로그(ln)가 적용되어야 할 자리에 상용로그(log10)가 오등록되어 있었습니다. 두 로그는 약 2.30배의 비례 상수가 개입하므로, 꼬리 영역의 결과 값이 일정 비율로 완전히 틀어지게 만드는 버그를 초래했습니다. 그러나 이 오류는 대다수의 일상적인 확률 범위 테스트에서 완벽하게 침묵하여 조기 감지되지 않았는데, 일상적인 정산 범위가 모두 다항식으로만 처리되는 중앙 분기를 탔기 때문입니다. NORM.S.INV(0.5) 결과인 0이나 NORM.S.INV(0.975) 결과인 교과서적인 1.959964 등은 모두 로그 연산 자체가 배제된 중앙 다항식에서 조율되었습니다. 이 오류는 NORM.S.INV(0.001)처럼 확률 범위가 꼬리 깊숙이 들어가야만 비로소 활성화되었고, 정상 수치인 -3.0902323 대신 약 2.30배 틀어진 비정상 값을 리턴해 냈습니다. 신뢰구간 분석 도구를 포함하여 역정규분포 꼬리 연산에 의존하는 모든 상위 함수들이 동일한 편향 오류를 물려받았습니다. 이 뼈아픈 교훈은 단순하지만 중요합니다: 분기 구조를 갖는 모든 함수는 개별 분기 내부를 골고루 조준하는 독립 테스트 포인트가 반드시 수립되어야 하며, 그렇지 않으면 정상 동작하는 다수의 경로가 소수의 치명적인 오류 경로를 완벽하게 은폐하기 때문입니다. 해결책은 상용로그 토큰을 자연로그 토큰으로 교정하는 단 한 줄의 조치였고, 이후 꼬리 영역 수치가 Excel과 정밀하게 합치되었습니다

x의 부호가 결정하는 t-분포의 꼬리 방향

스튜던트 t-누적 분포 함수 연산에는 부호를 거꾸로 잡기 쉬운 미세한 대칭 구조가 있습니다. CDF 결과 값은 df / (df + x²) 인수 조건에서 평가된 정규화된 불완전 베타 함수 값으로부터 유도되는데, 이 베타 결과값은 x의 누적 확률이 아닌 x의 절댓값을 벗어나는 양쪽 대칭 꼬리 영역의 확률 값(양측 꼬리 확률)입니다. t-분포의 좌우 대칭 특성상, 누적 확률을 도출하기 위한 보정 공식은 x의 부호가 0의 왼쪽에 있는지 오른쪽에 있는지에 따라 나뉩니다

// 스튜던트 t CDF. ib는 df/(df+x*x) 조건의 정규화된 불완전 베타이며,
// 보정하지 않고 ib를 그대로 리턴하면 오측 꼬리 확률이 나옵니다.
ib := BetaIF(df / 2, 0.5, df / (df + x * x));
if x > 0 then
  result := 1 - 0.5 * ib        // 평균 초과 영역: 1에서 꼬리 확률의 절반을 뺍니다
else if x < 0 then
  result := 0.5 * ib            // 평균 미만 영역: 꼬리 확률의 절반
else
  result := 0.5;                // 정확히 평균 지점

x가 0을 초과하면 누적 확률은 1에서 전체 대칭 꼬리 영역 확률의 절반을 감산한 값이며, x가 0 미만이면 그 꼬리 영역 확률의 절반 자체이고, x가 정확히 0이면 정확히 0.5입니다. 불완전 베타 함수 값을 그대로 가공 없이 반환하면 확률 분포의 꼬리 방향을 정반대로 왜곡하여, 0이 아닌 임의의 x에 대해 전체 곡선 면적만큼 틀어진 오류를 유도합니다. 우측 꼬리(Right-tail) 및 양측 꼬리(Two-tail) 버전의 t-분포 함수들 또한 이 동일한 분기 제어 로직을 공유하여 구축되며, 이에 따라 T.DIST.2T(1,1)은 0.5를, T.DIST(1,1,TRUE)는 0.75를 정확히 반환하고, 역함수인 T.INV 역시 이 정밀 CDF를 기준으로 이분 연산을 실행하므로 수치적 역연산 정합성이 완벽하게 보장됩니다

이 정교한 수치 해석 규칙들은 모두 셀 이면에 존재하며, 이는 설계자가 의도한 최적의 은폐 구조입니다. 개발자는 수식을 기입하고 Excel과 똑같은 수치 결과값만 확인하면 됩니다. 사용자 정의 함수(UDF)로 엔진을 추가 연동하는 규칙은 수식 엔진 및 커스텀 UDF 가이드에서 상세히 가이드하며, 교차 시트 참조 및 범위 이름을 통해 참조가 수립되는 구조는 이름 정의 및 교차 시트 수식 가이드에서 다룹니다. 이 정밀 수식 체계는 파일 입출력, 차트 작성 및 서식 제어 API 등과 함께 Delphi 및 C++Builder용 HotXLS 스프레딧 컴포넌트와 함께 일괄 제공됩니다