GUIDE 07

계산기의 부동소수점 오차: 0.1+0.2와 sin(π)가 이상한 이유

0.1+0.2가 0.30000000000000004가 되는 IEEE 754 배정밀도의 원리, 12자리 표시로 0.3이 보이는 이유, sin(π) 보정, 170!과 오버플로, 2⁵³ 정수 한계, 뺄셈 상쇄를 설명합니다.

프로그래밍 언어에서 0.1 + 0.2 를 출력하면 0.30000000000000004가 나온다. 계산기가 고장 난 것이 아니라 컴퓨터가 실수를 저장하는 방식 때문이다. 이 글에서는 SciKey를 포함한 대부분의 계산 소프트웨어가 쓰는 IEEE 754 배정밀도 형식의 원리와, 그 한계가 계산 결과에 어떻게 드러나는지, 계산기가 이를 어떻게 다루는지 설명한다.

1. IEEE 754 배정밀도

배정밀도(double, binary64) 실수는 64비트로 이루어진다.

부분비트 수역할
부호1양수·음수
지수112의 몇 제곱인지
가수52유효숫자(맨 앞의 1은 숨겨져 있어 실제 53비트)

53비트 유효숫자는 10진수로 약 15~17자리에 해당한다. 표현할 수 있는 가장 큰 수는 약 1.797×10³⁰⁸이고, 1 바로 다음 수와 1의 차이(기계 엡실론)는 2⁻⁵² ≈ 2.22×10⁻¹⁶이다.

2. 0.1은 2진수로 정확히 쓸 수 없다

10진수에서 1/3 = 0.333…이 끝나지 않듯, 2진수에서는 1/10이 끝나지 않는다. 0.1 = 0.0001100110011…₂로 무한히 반복된다. 53비트에서 잘라 반올림하므로 실제로 저장되는 값은

0.1000000000000000055511151231257827…

이다. 0.2도 마찬가지로 약간 크게 저장된다. 둘을 더하면 오차가 합쳐져 0.3000000000000000444…가 되는데, 이것은 0.3을 저장한 값(0.2999999999999999888…)과 다른 배정밀도 수다. 그래서 가장 짧게 출력하면 0.30000000000000004가 된다.

같은 이유로 0.1×3 도 0.30000000000000004다. 반면 0.5, 0.25, 0.375처럼 분모가 2의 거듭제곱인 소수는 2진수로 정확히 표현되어 오차가 없다.

3. 계산기에서는 왜 0.3으로 보이나

SciKey는 내부값을 그대로 보여 주지 않고 설정한 유효자릿수(기본 12자리)로 반올림해 표시한다. 0.30000000000000004를 12자리로 반올림하면 0.300000000000, 즉 0.3이다. 오차는 17번째 자리에 있어 12자리 표시에서는 보이지 않는다.

공학용 계산기도 같은 원리다. 흔히 내부적으로 15자리 정도를 계산하고 화면에는 10자리를 보여 주어, 끝자리의 작은 오차를 표시 단계에서 감춘다. 표시만 바꾸는 것이므로 내부값은 여전히 0.30000000000000004다. 이 값을 계속 이어서 계산해도 오차는 대개 10⁻¹⁶ 수준이라 실무에 영향이 없다.

4. sin(π)는 왜 0이 아닌가

RAD 모드에서 sin(π) 의 수학적 값은 0이다. 하지만 π는 무리수라서 배정밀도 π는 참값보다 약 1.22×10⁻¹⁶만큼 작다. sin은 π 근처에서 기울기가 −1이므로 sin(배정밀도 π) ≈ 1.2246×10⁻¹⁶이 된다. 계산 자체는 정확하게 했는데 입력이 이미 π가 아니었던 것이다.

SciKey는 다음 원칙으로 이런 값을 보정한다.

  1. DEG·GRAD 모드에서는 인수를 360°(400 gon)로 정확히 줄인 뒤, 90°의 배수이면 정확한 값을 돌려준다. sin(180) = 0, cos(90) = 0, tan(90) 은 정의되지 않으므로 오류.
  2. RAD 모드에서는 인수가 π의 정수배와 상대오차 10⁻¹³ 이내로 일치하면 sin을 0으로, π/2의 홀수배와 일치하면 cos를 0으로 보정하고 tan은 오류로 처리한다.

그래서 SciKey에서 sin(π) 는 0으로 나온다. 이 보정은 π의 배수 근처에서만 작동하며, 다른 값의 계산에는 영향을 주지 않는다.

5. 큰 수: 팩토리얼과 오버플로

팩토리얼은 빠르게 커진다. 170! ≈ 7.25741561531×10³⁰⁶은 배정밀도로 표현할 수 있지만, 171!은 약 1.24×10³⁰⁹로 최댓값 1.797×10³⁰⁸을 넘는다. 그래서 SciKey에서 n! 은 0~170의 정수만 받으며 171! 은 오버플로 오류다.

실무에서 큰 팩토리얼이 필요한 경우는 대개 비율을 구할 때다. 예를 들어 조합 C(200, 3)은 200!/(3!·197!)이지만 nCr(200, 3) = 1313400으로 바로 계산하면 중간값이 커지지 않는다. 확률 계산에서는 로그를 취해 더하는 방법도 쓴다.

6. 정수의 한계: 2⁵³

가수가 53비트이므로 2⁵³ = 9007199254740992까지의 정수는 모두 정확히 표현된다. 그보다 크면 사이사이 정수를 건너뛴다. 2⁵³ + 1은 표현할 수 없어서 2⁵³으로 반올림된다. 그래서 2^53+1-2^53 을 계산하면 1이 아니라 0이 나온다. 더한 1이 저장 단계에서 사라진 것이다.

SciKey가 2진·8진·16진 표시를 절댓값 2⁵³ 이하의 정수에서만 보여 주는 이유가 이것이다. 그보다 큰 수는 하위 자릿수가 정확하다고 보장할 수 없다. 16자리 넘는 카드번호나 ID를 숫자로 계산하면 끝자리가 바뀌는 것도 같은 현상이다.

7. 뺄셈 상쇄: 가장 위험한 오차

거의 같은 두 수를 빼면 앞자리가 사라지고 오차만 남는다. 이것을 뺄셈 상쇄(catastrophic cancellation)라 한다.

예제 1. (1+1E-15)-1 의 참값은 10⁻¹⁵이지만, 배정밀도 결과는 1.11022302463×10⁻¹⁵다. 1+10⁻¹⁵를 저장하는 순간 반올림 오차(약 10⁻¹⁶ 크기)가 생기고, 1을 빼면 그 오차가 결과의 11%나 된다.

예제 2. x = 10⁸일 때 √(x²+1) − x의 참값은 약 5×10⁻⁹이다. 그러나 그대로 계산하면 √(10¹⁶+1)이 10⁸과 구분되지 않아 결과가 0이 된다. 분자를 유리화해 같은 값을 다른 식으로 쓰면

√(x²+1) − x = 1 / (√(x²+1) + x)

이 되고, x에 10⁸을 저장한 뒤 1/(sqrt(x^2+1)+x) 를 계산하면 5×10⁻⁹로 정확히 나온다.

예제 3. 1 − cos(10⁻⁸)의 참값은 약 5×10⁻¹⁷인데, 그대로 계산하면 cos(10⁻⁸)이 1로 반올림되어 0이 된다. 항등식 1 − cos x = 2 sin²(x/2)를 쓰면 2sin(0.5E-8)^2 = 5×10⁻¹⁷로 정확하다(RAD 모드).

유효자릿수를 아무리 늘려도 이 문제는 해결되지 않는다. 식을 바꿔야 한다.

8. 실무 요령

  • 같음을 비교하지 않는다. 두 계산 결과가 같은지 볼 때는 차이가 충분히 작은지(예: 상대오차 10⁻¹²)를 본다.
  • 돈 계산은 원 단위 정수로. 0.1원 단위 오차가 쌓이지 않도록 가장 작은 단위를 정수로 다룬다.
  • 거의 같은 수의 뺄셈을 피한다. 유리화, 항등식, 식 정리로 뺄셈을 없앤다.
  • 큰 수는 비율로. 팩토리얼이나 거듭제곱이 커지면 nCr, 로그, 약분을 먼저 쓴다.
  • 중간 반올림은 하지 않는다. 배정밀도 오차는 10⁻¹⁶ 수준이지만, 손으로 4자리 반올림하면 10⁻⁴ 수준의 오차가 생긴다. 실제로 더 큰 오차는 대개 사람 쪽에서 나온다.
  • 표시 자릿수를 15로 올려 보기. 결과가 이상하면 유효자릿수를 늘려 내부값을 확인한다. 끝자리 오차인지, 식이 틀린 것인지 구분할 수 있다.

→ 계산기 열기 함수 레퍼런스

최종 수정: 2026-09-24