본문으로 건너뛰기

JavaScript에서 Number 타입의 문제점

Summary

JavaScript의 Number 타입은 IEEE 754 부동 소수점 표준을 기반으로 동작하며, 정밀도 문제, 안전한 정수 범위, 연산 오류 등의 한계를 가지고 있다. 이를 이해하고 적절한 대처법을 적용하는 것이 중요하다.

Details

Number 타입 개요

  • JavaScript에서 Number 타입은 64비트 IEEE 754 배정도 부동 소수점 형식을 사용한다.
  • 정수와 실수를 구분하지 않고 동일한 Number 타입으로 관리된다.
console.log(typeof 10); // 'number'
console.log(typeof 10.5); // 'number'

정밀도 문제

  • JavaScript의 Number 타입은 소수를 정확하게 표현하지 못하는 경우가 많다.
  • 이는 IEEE 754 부동 소수점 표준에서 이진수 변환 시 발생하는 오차 때문이다.
console.log(0.1 + 0.2); // 0.30000000000000004
console.log(0.1 + 0.2 === 0.3); // false

왜 0.1 + 0.2가 0.3이 아닌가?

  • 0.1과 0.2는 이진수로 정확하게 표현될 수 없는 값이다.
  • 실제 내부 저장 방식은 다음과 같다.
    • 0.10.00011001100110011001100110011001100110011001100110011... (2진수)
    • 0.20.0011001100110011001100110011001100110011001100110011... (2진수)
  • 이진수 연산 결과가 정확히 0.3이 되지 않기 때문에 오차가 발생한다.

안전한 정수 범위

JavaScript의 Number 타입은 안전한 정수(Safe Integer) 를 다음 범위 내에서만 정확하게 표현할 수 있다.

Image

console.log(Number.MAX_SAFE_INTEGER); // 9007199254740991
console.log(Number.MIN_SAFE_INTEGER); // -9007199254740991

이 범위를 벗어나면 연산 결과가 부정확할 수 있다.

console.log(9007199254740991 + 1); // 9007199254740992
console.log(9007199254740991 + 2); // 9007199254740992 (잘못된 결과)

연산 오류

부동 소수점 연산에서 발생하는 오류 중 하나는 0과 -0의 구분이다.

console.log(1 / 0); // Infinity
console.log(1 / -0); // -Infinity

-00은 비교 연산에서는 같지만, 연산 결과는 다를 수 있다.

해결 방법

1. BigInt 사용

  • BigInt는 정수를 안전하게 표현하는 별도의 데이터 타입이다.
  • n을 붙여서 BigInt 타입으로 변환할 수 있다.
console.log(9007199254740991n + 2n); // 9007199254740993n
  • 단, BigIntNumber와 혼합해서 사용할 수 없다.
console.log(10n + 5); // TypeError: Cannot mix BigInt and other types

2. 라이브러리 활용

  • 부동 소수점 오차를 방지하기 위해 decimal.js, big.js, bignumber.js와 같은 라이브러리를 사용할 수 있다.
const Big = require('big.js');
console.log(new Big(0.1).plus(0.2).toString()); // "0.3"

Reference

issue: Related issue in this repo

author note: Related note in this repo

link: External reference