본문 바로가기
Language/JavaScript

[타입] 왜 BigInt를 사용할까?

by 선서니 2023. 10. 29.

입사 2주차, 팀장님에게 BigInt를 사용하는 이유가 뭔지 질문을 받았다.

 

자바스크립트에서 정수를 안전하게 표현할 수 있는 범위를 넘어갈 때 사용하기 위해서 입니다!

라고 답했다. 그것도 있지만 더 중요한 게 있다고 하셨다.

이걸 찾는 걸 숙제로 주셨다.

 

숙제를 받고 나서 제일 먼저 든 생각은

이것말고 다른 게 더 있다고?

였다. 왜냐하면 답한 내용은 mdn 문서에서 찾은 내용이어서 확실하다고 생각했기 때문이다.(찾은 내용을 정확하게 이해하지 않고 빨리 답해야 한다는 생각에 본대로 얘기를 했다...ㅠ)

 

그런데 mdn 문서를 곱씹으면서 곰곰이 생각해보니, 자바스크립트는 숫자 타입을 정수, 실수 나누지 않고 Number 라는 하나의 타입으로 처리한다. 그리고 저기서 말하는 "안전하게"는 정확히 어떤 의미지? 하는 생각이 들었다.

 

궁금했던 내용들을 찾고 답을 찾아간 과정을 다시 한 번 정리해보려고 한다.

 

 

자바스크립트의 Number 타입

자바스크립트는 정수, 실수를 구분하지 않고 Number라는 하나의 타입으로 숫자를 다룬다. Number는 배정밀도 부동소수점 숫자로 알려진 64비트 형식의 IEEE-754으로 처리된다.  우리가 일반적으로 정수라고 생각하는 37도 자바스크립트 코드에서는 부동 소수점 값이다.

 

먼저, Number  타입 리터럴을 보면 다양한 방식으로 표현할 수 있다.

7; // 정수 리터럴
2.5; // 부동 소수점 리터럴
0b111; // 2진수 리터럴 (binary literal)
0o777; // 8진수 리터럴 (octal literal)
0xf5; // 16진수 리터럴 (hexademical literal)
10_000 // 숫자 구분 기호 (Numeric Separators)

 

이처럼 표현되는 수는 다양하지만 모든 Number 타입 리터럴에 typeof 연산을 해보면 'number' 라는 결과가 나온다.

typeof 1; // 'number'
typeof 0x4d; // 'number'
typeof 10_000; // 'number'

 

 

부동 소수점(floating point)

아래 계산을 콘솔에 하게 되면 예상했던 0.3이 아니라 다른 결과가 나오게 된다.

0.1 + 0.2;
> 0.30000000000000004

 

이 이유는 앞에서 말했던 것처럼 자바스크립트가 숫자(Number)를 64비트 부동소수점으로 저장하기 때문이다. 

 

부동 소수점 방식은 소수점(point)의 위치가 고정되지 않고 둥둥 떠다닌다(floating)라는 의미로 표현할 수 있는 값의 범위를 최대한 넓혀 오차를 줄이자는 시도에서 탄생했다.

 

메모리를 가수부(52bit)와 지수부(11bit)로 나눈다. 

[출처] 인파  실수 표현(부동 소수점) 원리 한눈에 이해하기

 

64비트 중 52비트는 숫자를 저장하는데 사용되고, 11비트는 소수점의 위치를 나타내는 데, 1비트는 부호를 저장하는데 사용된다.

 

 

부정확한 계산

다시 0.1 + 0.2의 이상한 계산 결과로 돌아가본다면,

0.1 + 0.2;
> 0.30000000000000004

 

컴퓨터는 모든 데이터를 2진법으로 저장하는데, 어떤 소수들은 무한 소수가 된다. 바로 0.1과 0.2가 있다!  컴퓨터는 저장 공간에 한계가 있기 때문에 무한 소수를 유한소수로 바꿔 저장한다. 이 과정에서 정확한 값이 아니라 오차가 발생하게 된다.

 

자바스크립트의 경우 64비트 초과하는 부분을 반올림해서 저장한다.

두 무한 소수 0.1과 0.2의 합인 0.3도 무한 소수가 된다. 따라서 자바스크립트에서 64비트를 초과해 근사치로 저장되기 때문에 예상했던 값과 오차가 발생한다.

 

이는 자바스크립트만의 문제가 아니라 다른 모든 언어에서도 발생하는 문제이다. 다만 자바스크립트는 모든 숫자를 동일한 Number 타입으로 선언하기 때문에 연산 오류에 취약하다.

 

아주 근소한 차이이긴 하지만 금융과 같은 아주 사소한 차이도 허락되지 않는 분야는 이런 오차가 아주 큰 문제가 될 수 있다. 

 

 

어디까지가 정확한 범위일까?

그럼 다시 제일 처음으로 돌아가서 

자바스크립트에서 정수를 안전하게 표현할 수 있는 범위를 넘어갈 때 사용하기 위해서 입니다!

여기서 말하는 "안전하게"라는 것의 의미는 어떤 것일까?

 

자바스크립트가 숫자를 표현하는 64비트 부동 소수점 방식에서는 -(2^53 - 1)부터 2^53 - 1 사이의 수가 안전하다. 여기서 안전하다의 의미는 정수를 정확하고 올바르게 비교할 수 있는 것을 의미한다.  즉, -9007199254740991 ~ 9007199254740991 사이의 값이 안전한 정수 값이다

 

안전한 숫자 범위를 넘어서면 부정확한 연산 결과가 나온다.

const x = 9007199254740991;
console.log(x + 2);
// 9007199254740992
console.log(x + 4);
// 9007199254740996
console.log(x + 6);
// 9007199254740996

const y = -9007199254740991;
console.log(y - 2);
// -9007199254740992
console.log(y - 4);
// -9007199254740996
console.log(y - 6);
// -9007199254740996

 

 

정확한 연산 결과를 얻는 방법

그렇다면 정확한 연산 결과를 얻으려면 얻는 방법은 없을까?

 

1. 실수 연산을 할 때

내장 메서드를 통해 실수를 어림수로 만들어 계산하기

내장 메서드 만으로 부족하다면 관련 라이브러리를 통해 해결하기

 

 

2. 아주 큰 정수만 다뤄도 될때

BigInt 를 사용해 해결할 수 있다!

BigInt는 정수 리터럴 뒤에 n을 덧붙이거나 BigInt()로 생성할 수 있다.

const theBiggestInt = 9007199254740991n;

const alsoHuge = BigInt(9007199254740991);
// ↪ 9007199254740991n

const hugeString = BigInt("9007199254740991");
// ↪ 9007199254740991n

const hugeHex = BigInt("0x1fffffffffffff");
// ↪ 9007199254740991n

const hugeBin = BigInt(
  "0b11111111111111111111111111111111111111111111111111111",
);
// ↪ 9007199254740991n

 

 

정리

  • 자바스크립트는 Number 타입 하나로 정수, 실수 모든 수를 다루고 있다.
  • 자바스크립트의 Number타입은 64비트 부동 소수점 방식으로 처리된다.
  • 컴퓨터는 저장 공간의 제한이 있기 때문에 Number 타입에 할당된 64비트를 넘어가는 수는 반올림하여 근사치로 저장한다.
  • 이 과정에서 정확한 값과 다른 오차가 발생한다. >> 정확성의 오류가 발생
오차없이 정확하게 큰 정수를 다루고자 할 때 BigInt를 사용할 수 있다.

 

 

참고

number 타입

 

number 타입 | JavaScript로 만나는 세상

처음 시작하는 사람들을 위한 JavaScript 교재

helloworldjavascript.net

부동소수점 실수 탐구 in JavaScript

 

부동소수점 실수 탐구 in JavaScript

자바스크립트에서 사용하는 64비트 IEEE 754 형식에 대해 알아보았다.

velog.io

숫자형

 

숫자형

 

ko.javascript.info

BigInt_MDN

 

BigInt - JavaScript | MDN

BigInt 는 Number 원시 값이 안정적으로 나타낼 수 있는 최대치인 2^53 - 1보다 큰 정수를 표현할 수 있는 내장 객체입니다.

developer.mozilla.org

Number_MDN

 

Number - JavaScript | MDN

Number 생성자는 숫자를 다루기 위해 상수와 메소드를 가지고 있습니다. 다른 타입의 값은 Number() 함수를 사용하여 숫자로 바꿀 수 있습니다.

developer.mozilla.org

실수 표현(부동 소수점) 원리 한눈에 이해하기

 

☕ 실수 표현(부동 소수점) 원리 한눈에 이해하기

실수의 2진수 표현 10진수의 정수를 2진수의 정수로 변환할 수 있듯이, 10진수의 소수를 2진수의 소수로 변환할 수 있다. 예를들어 10진수 11.765625 를 2진수 소수로 변환하는 방법은 다음과 같다. 먼

inpa.tistory.com

 

 

 

 

 

 

 

 

 

 

'Language > JavaScript' 카테고리의 다른 글

[JavaScript] Promise 배열 다루기  (0) 2024.01.27

댓글