0.1 + 0.2가 0.3이 아니라고?

"0.1 + 0.2가 0.3이 아니라고?" 프론트엔드 개발을 하다 보면 한 번쯤은 마주하게 되는 순간입니다. JavaScript의 부동소수점 연산 이슈의 원인과 실무에서 활용할 수 있는 해결책들에 대해 정리해두어 같은 이슈를 만났을 때 당황하지 않고 해결하고자 합니다.

문제의 발견

console.log(0.1 + 0.2 === 0.3); // false
console.log(0.1 + 0.2); // 0.30000000000000004
console.log(0.1 + 0.2 + 0.3); // 0.6000000000000001
console.log(0.1 * 3); // 0.30000000000000004
console.log(0.3 - 0.1); // 0.19999999999999998

JavaScript가 수학을 못한다고 생각할 수 있지만, 결론부터 이야기하면 컴퓨터 연산의 근본적인 한계 때문입니다.

JavaScript 64비트 숫자의 한계(부동소수점과 고정소수점)

1. 이진법 표현의 한계

컴퓨터는 모든 것을 이진법으로 처리합니다. 십진법에서 간단한 0.1도 이진법으로는 무한소수가 됩니다. 0.1 (십진법) = 0.0001100110011001100... (이진법) JavaScript는 IEEE 754 표준에 따라 64비트로 숫자를 저장하는데, 무한소수를 유한한 비트로 표현하다 보니 반올림 오차가 발생합니다.

2. 부동소수점 구조

64비트 부동소수점은 다음과 같이 구성됩니다:

  • 부호 비트: 1비트 (양수/음수)

  • 지수 비트: 11비트 (범위 결정)

  • 가수 비트: 52비트 (정밀도 결정)

모든 십진수를 정확히 표현할 수는 없습니다.(Number.MAX_SAFE_INTEGER는 16자리, 15자리는 안전)

부동소수점

  • 넓은 범위, 하지만 근사값

  • 가수 + 지수로 소수점이 "떠다님"

  • Bias(64bit 기준 1023)으로 음수/양수 지수 표현

고정소수점

  • 제한된 범위, 하지만 정확함

  • 정수부 + 소수부로 소수점이 "고정"

  • 정수 연산으로 정확한 계산

실무에서 마주하는 상황들

1. 가격 계산

const price = 19.90;
const tax = 0.08;
const total = price * (1 + tax);
console.log(total); // 21.491999999999997
console.log(`총 가격: $${total.toFixed(2)}`); // $21.49

2. 좌표 계산

let position = 0;
const increment = 0.1;

for (let i = 0; i < 10; i++) {
  position += increment;
}
console.log(position); // 0.9999999999999999 (예상: 1.0)

3. 금융 계산

const principal = 1000;
const rate = 0.05;
const time = 12;
const interest = principal * rate * time;
console.log(interest); // 599.9999999999999

해결방법?

  • 소수점 자리 제한

    const a = 0.1;
    const b = 0.2;
    console.log((a + b).toFixed(1)); // "0.3"
  • 정수 변환

    const a = 0.1;
    const b = 0.2;
    const multiplier = Math.pow(10, 1);
    
    const sum = (a * multiplier) + (b * multiplier); // 3
    
    console.log(sum / multiplier); // 0.3
  • 라이브러리 사용(big.js, decimal.js 등)

    • 배열 기반 십진수 표현

      const Big = require('big.js');
      
      const a = new Big(0.1);
      const b = new Big(0.2);
      
      const sum = a.plus(b);
      console.log(sum.toString());  // "0.3"
      const Decimal = require('decimal.js');
      
      const a = new Decimal(0.1);
      const b = new Decimal(0.2);
      
      const sum = a.plus(b);
      console.log(sum.toString());  // "0.3"

자바스크립트만 그럴까?

부동소수점 이슈는 JavaScript만의 버그가 아니라 근본적인 특성입니다. Java와 같은 언어들도 같은 상황에서의 문제가 발생하고 있는데요. 중요한 것은 이 한계와 쓰임을 알고 적절한 해결책을 선택하는 것 같습니다.

참고

Last updated