도구스개발

부동소수점 누적오차·카한 합 계산기

0.1을 10000번 더하면 왜 1000이 아닌지 보여 주고, 순차합·역순합·정렬 후 합·짝지어 더하기·카한 합·노이마이어 합의 오차를 나란히 놓습니다. 정확한 합은 반올림 없는 Shewchuk 방식으로 구합니다.

어떤 값들을 더하느냐에 따라 어느 방법이 이기고 지는지가 갈립니다.

많을수록 순차합의 오차가 쌓입니다.

10,000개를 더한 정확한 합

1000

그냥 앞에서부터 더하면 1000.0000000001588가 나와 1.588e-10만큼 어긋납니다. 정확한 합은 반올림을 전혀 하지 않는 Shewchuk 방식으로 구한 값입니다.

방법별 결과

방법결과오차상대오차
순차합 (앞에서부터)1000.00000000015881.59e-101.6e-13
역순합 (뒤에서부터)1000.00000000015881.59e-101.6e-13
정렬 후 합 (작은 것부터)1000.00000000015881.59e-101.6e-13
짝지어 더하기 (pairwise)10000 (정확)0
카한 보정합10000 (정확)0
노이마이어 보정합10000 (정확)0
  • 순차합 (앞에서부터)가장 흔한 방식. 오차가 O(nε)로 쌓인다.
  • 역순합 (뒤에서부터)값은 같은데 순서만 다르다.
  • 정렬 후 합 (작은 것부터)작은 항을 먼저 모아 두면 큰 값에 묻히는 것을 줄인다.
  • 짝지어 더하기 (pairwise)둘씩 재귀로 합쳐 O(ε·log n). 보정 없이 비용도 그대로다.
  • 카한 보정합잃은 몫을 c에 담아 되돌린다. 오차가 n과 무관해진다.
  • 노이마이어 보정합큰 항이 뒤에 와도 잃지 않도록 카한을 고친 것.
항 개수10,000개
정확한 합1000
순서를 뒤집으면결과가 같습니다
순차합 오차 ÷ 카한 오차카한은 오차가 0입니다
이론 상한 n·ε (상대)2.220e-12
machine epsilon ε2.2204e-16
카한 보정은 잘려 나간 조각을 기억해 두었다가 다음 항에 되돌려 주는 것입니다. y = x − c로 지난번 손실을 미리 갚고, c = (t − s) − y로 이번에 잃은 몫을 다시 담습니다. 이렇게 하면 오차 상한이 n에 무관한 O(ε)가 되어, 항이 백만 개여도 자릿수를 거의 잃지 않습니다.
카한도 지는 자리가 있습니다. 더하는 항이 누적합보다 클 때가 그렇습니다. 「1, 1e16, 1, −1e16 되풀이」 묶음을 골라 보면 순차합과 카한이 함께 틀리고 노이마이어만 맞히는 것을 볼 수 있습니다. 노이마이어는 둘 중 큰 쪽을 기준으로 잃은 몫을 잡습니다.
실무에서 조심할 것 하나. 컴파일러가 「(t − s) − y는 어차피 0」이라고 판단해 보정을 통째로 지워 버릴 수 있습니다. C·C++의 -ffast-math가 대표적입니다. 자바스크립트는 IEEE 754 의미론을 지키도록 정해져 있어 이 최적화가 일어나지 않습니다.

사용 방법

  1. 1더할 값 묶음을 고릅니다. 「0.1을 n번」이 출발점으로 좋습니다.
  2. 2항 개수를 늘려 가며 순차합의 오차가 어떻게 쌓이는지 봅니다.
  3. 3방법별 표에서 어느 방법이 정확한 합을 맞혔는지 확인합니다.
  4. 4「1, 1e16, 1, −1e16 되풀이」를 골라 카한이 지고 노이마이어가 이기는 자리를 확인합니다.

자주 묻는 질문

0.1이 2진수로 딱 떨어지지 않아 조금 큰 값으로 저장되고, 더할 때마다 반올림 오차가 쌓이기 때문입니다. 순차합은 1000.0000000001588쯤이 됩니다. 재미있게도 이 경우 정확한 합은 정확히 1000인데, 저장된 0.1의 초과분을 1만 배 해도 1000 근처 double 간격의 절반을 넘지 못해 1000으로 반올림되기 때문입니다.

달라집니다. 부동소수점 덧셈에는 결합법칙이 성립하지 않기 때문입니다. 누적합이 커진 뒤에 작은 항을 더하면 그 항의 하위 비트가 자리를 잃고 잘려 나갑니다. 1e16에 1을 1000번 더하면 앞에서부터는 하나도 반영되지 않지만, 작은 것부터 모아 더하면 1000이 온전히 남습니다.

잘려 나간 조각을 보정항 c에 기억해 두었다가 다음 항에 되돌려 주기 때문입니다. y = x − c로 지난 손실을 미리 갚고, c = (t − s) − y로 이번에 잃은 몫을 다시 담습니다. 오차 상한이 항 개수 n에 무관한 O(ε)가 되어, 항이 백만 개여도 자릿수를 거의 잃지 않습니다.

있습니다. 더하는 항이 누적합보다 클 때입니다. [1, 1e16, 1, −1e16]에서 정답은 2인데 순차합도 카한도 0을 냅니다. 누적합이 1일 때 1e16을 더하면 1이 통째로 잘려 나가는데 카한의 보정항이 그 손실을 잡지 못하기 때문입니다. 노이마이어 합은 둘 중 큰 쪽을 기준으로 손실을 잡아 이 경우까지 맞힙니다.

배열을 반씩 나눠 재귀로 합치는 방법입니다. 오차가 O(ε·log n)으로 줄고 보정 계산이 없어 속도도 순차합과 거의 같습니다. NumPy의 sum이 이 방식을 씁니다. 다만 카한·노이마이어만큼 강하지는 않습니다.

실수 산술의 결합법칙을 가정하는 최적화가 켜져 있으면 (t − s) − y를 0으로 간주해 보정 코드를 통째로 지워 버릴 수 있다는 뜻입니다. C·C++의 -ffast-math가 대표적입니다. 자바스크립트는 IEEE 754 의미론을 지키도록 정해져 있어 이 계산기에서는 그런 일이 일어나지 않습니다.

전송되지 않습니다. 모든 계산은 브라우저 안에서 이뤄지고, 입력값은 이 기기에만 남습니다.

알아두면 좋은 점

  • 정확한 합은 Shewchuk의 겹치지 않는 부분합 확장으로 구합니다. 파이썬 math.fsum과 같은 방법이며 중간에 반올림을 전혀 하지 않아 정확히 반올림된 합을 냅니다. 각 방법의 오차는 이 값과의 차입니다.
  • 정답지 자체를 먼저 검증했습니다. [1, 1e100, 1, −1e100]에서 순차합이 0인데 정확한 합이 2인 것, 0.1을 열 번 더한 정확한 합이 1인 것, 순서를 섞어도 값이 같은 것을 테스트로 고정했습니다.
  • 카한이 지고 노이마이어가 이기는 경우와 그 반대 경우를 모두 고정했습니다. [1, 1e16, 1, −1e16]에서는 카한이 0을 내고, 순서를 [1e16, 1, 1, −1e16]으로 바꾸면 카한도 2를 맞힙니다 — 성패가 값의 순서에 달렸다는 것을 보이는 자리입니다.
  • 예제 묶음은 시드를 고정한 값이라 서버에서 그린 것과 브라우저에서 계산한 것이 같습니다. 하이드레이션이 어긋나지 않게 하려는 것입니다.
  • 이 계산기가 다루는 것은 double(배정밀도) 하나뿐입니다. 실제로는 float(단정밀도)에서 오차가 훨씬 빨리 쌓이고, 십진 소수 타입이나 정수 고정소수점으로 바꾸는 것이 더 나은 문제도 많습니다.
  • 항 개수는 200만까지 받습니다. 정확한 합 계산이 부분합을 여럿 들고 있어야 해서, 값의 크기가 아주 다양하면 그만큼 느려질 수 있습니다.

함께 보면 좋은 도구

마지막 검증: 2026년 9월 2일 · 결과는 참고용 추정치입니다.