모든 아티클

IEEE 754: 코드에서 0.1 + 0.2가 0.3과 같지 않은 이유

코틀린 REPL을 열고 0.1 + 0.2를 입력하면 0.3이 아니라 0.30000000000000004가 나옵니다. 안드로이드 공식 언어로 채택된 코틀린이라고 예외는 아닙니다 — 이는 특정 언어의 버그가 아니라 IEEE 754 표준이 분수를 2진법으로 표현하는 방식의 직접적인 결과이며, 자바스크립트, 자바, 파이썬 등 같은 표준을 쓰는 거의 모든 언어에서 동일하게 나타납니다.

부동소수점 숫자의 세 부분

IEEE 754에 따른 숫자는 부호(양수 또는 음수), 가수(숫자의 유효 자릿수), 지수(소수점을 이동시키는 거듭제곱) 세 요소로 저장됩니다. 이는 과학적 표기법과 비슷합니다: 1.5 × 10², 다만 2진법입니다.

0.1이 2진법으로 정확히 표현될 수 없는 이유

10진법의 0.1은 2진법에서 무한소수입니다(10진법의 1/3이 무한소수인 것과 비슷합니다). 컴퓨터는 제한된 수의 가수 비트만 저장하므로, 숫자는 표현 가능한 가장 가까운 값으로 반올림됩니다 — 바로 이 반올림이 눈에 보이는 "오차"를 만듭니다.

실제로 문제가 되는 지점: 결제 금액 합산

쇼핑몰 결제 페이지가 상품 가격을 단순 실수(float)로 계속 더하면, 상품 수가 많아질수록 예상 합계에서 몇 원씩 어긋날 수 있습니다 — 덧셈 한 번마다 아주 작은 반올림이 발생하고, 이 오차는 서로 상쇄되지 않고 누적됩니다. 결제 시스템이 원 단위 정수나 전용 decimal 타입으로 계산하고 float를 직접 더하지 않는 이유가 여기에 있습니다.

왜 필요한가

  • 금융이나 과학 계산에서 예상치 못한 반올림 오차를 이해하고 진단하기.
  • 특정 부동소수점 숫자의 정확한 2진 표현 확인하기.
  • 분수를 정확한 동등성으로 비교하는 것이 왜 나쁜 관행인지 동료나 학생에게 설명하기.

0 근처의 비정규화 수

숫자 값이 일반적인 표현(암묵적인 1로 시작하는 유효숫자)에 담기에 너무 작을 때, IEEE 754는 비정규화(subnormal) 모드로 전환하여 이 암묵적인 1을 버립니다 — 이를 통해 절대값이 더 작은 숫자를 갑작스러운 0으로의 도약 대신 점진적인 정밀도 손실을 감수하며 표현할 수 있습니다. 이런 "부드러운" 0으로의 전환은 갑작스러운 0 처리보다 낫지만, 일부 프로세서에서는 비정규화 연산이 일반 연산보다 눈에 띄게 느립니다.

도구 사용해보기