JavaScript의 최신 데이터 유형에 대한 필수 가이드: BigInt

게시 됨: 2022-03-10
빠른 요약 ↬ JavaScript에서 Number 유형은 2 53 보다 큰 정수 값을 안전하게 표현할 수 없습니다. 이 제한으로 인해 개발자는 비효율적인 해결 방법과 타사 라이브러리를 사용해야 했습니다. BigInt 는 이를 수정하기 위한 새로운 데이터 유형입니다.

BigInt 데이터 유형은 JavaScript 프로그래머가 Number 데이터 유형이 지원하는 범위보다 큰 정수 값을 나타낼 수 있도록 하는 것을 목표로 합니다. 임의의 정밀도로 정수를 나타내는 기능은 큰 정수에 대해 수학 연산을 수행할 때 특히 중요합니다. BigInt 를 사용하면 정수 오버플로가 더 이상 문제가 되지 않습니다.

또한 해결 방법을 사용하지 않고도 고해상도 타임스탬프, 큰 정수 ID 등으로 안전하게 작업할 수 있습니다. BigInt 는 현재 3단계 제안입니다. 사양에 추가되면 JavaScript에서 두 번째 숫자 데이터 유형이 되어 지원되는 총 데이터 유형 수가 8개가 됩니다.

  • 부울
  • 없는
  • 한정되지 않은
  • 숫자
  • BigInt
  • 상징
  • 물체

이 기사에서는 BigInt 를 잘 살펴보고 JavaScript에서 Number 유형의 한계를 극복하는 데 어떻게 도움이 되는지 알아보겠습니다.

문제

JavaScript에 명시적 정수 유형이 없다는 것은 다른 언어에서 온 프로그래머에게 종종 당혹스럽습니다. 많은 프로그래밍 언어는 float, double, integer 및 bignum과 같은 여러 숫자 유형을 지원하지만 JavaScript에서는 그렇지 않습니다. JavaScript에서 모든 숫자는 IEEE 754-2008 표준에 정의된 배정밀도 64비트 부동 소수점 형식으로 표시됩니다.

점프 후 더! 아래에서 계속 읽기 ↓

이 표준에서는 정확하게 표현할 수 없는 매우 큰 정수는 자동으로 반올림됩니다. 정확히 말하면 JavaScript의 Number 유형은 -9007199254740991(-(2 53 -1))에서 9007199254740991(2 53 -1) 사이의 정수만 안전하게 표현할 수 있습니다. 이 범위를 벗어나는 모든 정수 값은 정밀도를 잃을 수 있습니다.

이것은 다음 코드를 실행하여 쉽게 확인할 수 있습니다.

 console.log(9999999999999999); // → 10000000000000000

이 정수는 JavaScript가 Number 프리미티브로 안정적으로 나타낼 수 있는 가장 큰 수보다 큽니다. 따라서 둥근 모양입니다. 예상치 못한 반올림은 프로그램의 안정성과 보안을 손상시킬 수 있습니다. 다음은 또 다른 예입니다.

 // notice the last digits 9007199254740992 === 9007199254740993; // → true

JavaScript는 JavaScript에서 최대 안전한 정수를 빠르게 얻을 수 있는 Number.MAX_SAFE_INTEGER 상수를 제공합니다. 마찬가지로 Number.MIN_SAFE_INTEGER 상수를 사용하여 최소 안전 정수를 얻을 수 있습니다.

 const minInt = Number.MIN_SAFE_INTEGER; console.log(minInt); // → -9007199254740991 console.log(minInt - 5); // → -9007199254740996 // notice how this outputs the same value as above console.log(minInt - 4); // → -9007199254740996

해결책

이러한 제한 사항을 해결하기 위해 일부 JavaScript 개발자는 String 유형을 사용하여 큰 정수를 나타냅니다. 예를 들어 Twitter API는 JSON으로 응답할 때 개체에 ID의 문자열 버전을 추가합니다. 또한 bignumber.js와 같은 많은 라이브러리가 큰 정수 작업을 더 쉽게 하기 위해 개발되었습니다.

BigInt 를 사용하면 애플리케이션에서 더 이상 Number.MAX_SAFE_INTEGERNumber.Min_SAFE_INTEGER 이상의 정수를 안전하게 나타내기 위한 해결 방법이나 라이브러리가 필요하지 않습니다. 큰 정수에 대한 산술 연산은 이제 정밀도 손실 위험 없이 표준 JavaScript에서 수행할 수 있습니다. 타사 라이브러리보다 기본 데이터 유형을 사용하면 런타임 성능이 향상된다는 추가 이점이 있습니다.

BigInt 를 생성하려면 정수 끝에 n 을 추가하기만 하면 됩니다. 비교하다:

 console.log(9007199254740995n); // → 9007199254740995n console.log(9007199254740995); // → 9007199254740996

또는 BigInt() 생성자를 호출할 수 있습니다.

 BigInt("9007199254740995"); // → 9007199254740995n

BigInt 리터럴은 2진법, 8진법 또는 16진법 표기법으로도 작성할 수 있습니다.

 // binary console.log(0b100000000000000000000000000000000000000000000000000011n); // → 9007199254740995n // hex console.log(0x20000000000003n); // → 9007199254740995n // octal console.log(0o400000000000000003n); // → 9007199254740995n // note that legacy octal syntax is not supported console.log(0400000000000000003n); // → SyntaxError

BigInt는 동일한 유형이 아니기 때문에 BigInt 를 일반 숫자와 비교할 때 완전 항등 연산자를 사용할 수 없습니다.

 console.log(10n === 10); // → false console.log(typeof 10n); // → bigint console.log(typeof 10); // → number

대신 피연산자를 비교하기 전에 암시적 유형 변환을 수행하는 등호 연산자를 사용할 수 있습니다.

 console.log(10n == 10); // → true

단항 더하기( + ) 연산자를 제외한 모든 산술 연산자는 BigInt 에서 사용할 수 있습니다.

 10n + 20n; // → 30n 10n - 20n; // → -10n +10n; // → TypeError: Cannot convert a BigInt value to a number -10n; // → -10n 10n * 20n; // → 200n 20n / 10n; // → 2n 23n % 10n; // → 3n 10n ** 3n; // → 1000n let x = 10n; ++x; // → 11n --x; // → 10n

단항 더하기( + ) 연산자가 지원되지 않는 이유는 일부 프로그램이 + 가 항상 Number 를 생성하거나 예외를 발생시키는 불변에 의존할 수 있기 때문입니다. + 의 동작을 변경하면 asm.js 코드도 손상됩니다.

당연히 BigInt 피연산자와 함께 사용되는 경우 산술 연산자는 BigInt 값을 반환해야 합니다. 따라서 나누기( / ) 연산자의 결과는 자동으로 잘립니다. 예를 들어:

 25 / 10; // → 2.5 25n / 10n; // → 2n

암시적 유형 변환

암시적 형식 변환은 정보를 잃을 수 있기 때문에 BigIntNumber 사이의 혼합 작업은 허용되지 않습니다. 큰 정수와 부동 소수점 숫자를 혼합하면 결과 값이 BigInt 또는 Number 로 정확하게 표현되지 않을 수 있습니다. 다음 예를 고려하십시오.

 (9007199254740992n + 1n) + 0.5

이 표현식의 결과는 BigIntNumber 도메인 외부에 있습니다. 소수 부분이 있는 NumberBigInt 로 정확하게 변환될 수 없습니다. 그리고 2 53 보다 큰 BigInt 는 정확하게 Number 로 변환될 수 없습니다.

이 제한 사항으로 인해 NumberBigInt 피연산자를 혼합하여 산술 연산을 수행할 수 없습니다. 또한 Number 가 필요한 Web API 및 내장 JavaScript 함수에 BigInt 를 전달할 수 없습니다. 그렇게 하려고 하면 TypeError 가 발생합니다.

 10 + 10n; // → TypeError Math.max(2n, 4n, 6n); // → TypeError

관계 연산자는 다음 예와 같이 이 규칙을 따르지 않습니다.

 10n > 5; // → true

BigIntNumber 를 사용하여 산술 계산을 수행하려면 먼저 작업이 수행되어야 하는 도메인을 결정해야 합니다. 그렇게 하려면 Number() 또는 BigInt() 를 호출하여 피연산자 중 하나를 변환하기만 하면 됩니다.

 BigInt(10) + 10n; // → 20n // or 10 + Number(10n); // → 20

Boolean 컨텍스트에서 발생하면 BigIntNumber 와 유사하게 처리됩니다. 즉, BigInt0n 이 아닌 한 진실 값으로 간주됩니다.

 if (5n) { // this code block will be executed } if (0n) { // but this code block won't }

배열을 정렬할 때 BigIntNumber 유형 간의 암시적 유형 변환이 발생하지 않습니다.

 const arr = [3n, 4, 2, 1n, 0, -1n]; arr.sort(); // → [-1n, 0, 1n, 2, 3n, 4]

| 와 같은 비트 연산자 , & , << , >>^BigInt 에서 Number 와 유사한 방식으로 작동합니다. 음수는 무한 길이의 2의 보수로 해석됩니다. 혼합 피연산자는 허용되지 않습니다. 여기 예시들이 있습니다 :

 90 | 115; // → 123 90n | 115n; // → 123n 90n | 115; // → TypeError

BigInt 생성자

다른 기본 유형과 마찬가지로 BigInt 는 생성자 함수를 사용하여 생성할 수 있습니다. BigInt() 에 전달된 인수는 가능한 경우 BigInt 로 자동 변환됩니다.

 BigInt("10"); // → 10n BigInt(10); // → 10n BigInt(true); // → 1n

변환할 수 없는 데이터 유형 및 값은 예외를 throw합니다.

 BigInt(10.2); // → RangeError BigInt(null); // → TypeError BigInt("abc"); // → SyntaxError

생성자를 사용하여 생성된 BigInt 에 대해 산술 연산을 직접 수행할 수 있습니다.

 BigInt(10) * 10n; // → 100n

완전 항등 연산자의 피연산자로 사용되는 경우 생성자를 사용하여 생성된 BigInt 는 일반 것과 유사하게 처리됩니다.

 BigInt(true) === 1n; // → true

라이브러리 기능

JavaScript는 BigInt 값을 부호 있는 정수 또는 부호 없는 정수로 나타내기 위한 두 가지 라이브러리 함수를 제공합니다.

  • BigInt.asUintN(width, BigInt) : 0과 2 너비 -1 사이에서 BigInt 를 래핑합니다.
  • BigInt.asIntN(width, BigInt) : -2 너비 -1과 2 너비 -1 -1 사이에서 BigInt 를 래핑합니다.

이러한 함수는 64비트 산술 연산을 수행할 때 특히 유용합니다. 이렇게 하면 의도한 범위 내에 머물 수 있습니다.

브라우저 지원 및 트랜스파일

이 글을 쓰는 시점에서 Chrome +67 및 Opera +54는 BigInt 데이터 유형을 완벽하게 지원합니다. 불행히도 Edge와 Safari는 아직 구현하지 않았습니다. Firefox는 기본적으로 BigInt 를 지원하지 않지만 about:config 에서 javascript.options.biginttrue 로 설정하여 활성화할 수 있습니다. 지원되는 브라우저의 최신 목록은 Can I use…에서 확인할 수 있습니다.

불행히도 BigInt 를 트랜스파일하는 것은 런타임 성능 저하를 초래하는 매우 복잡한 프로세스입니다. 제안이 여러 기존 연산자의 동작을 변경하기 때문에 BigInt 를 직접 폴리필하는 것도 불가능합니다. 현재로서는 더 나은 대안이 BigInt 제안의 순수 JavaScript 구현인 JSBI 라이브러리를 사용하는 것입니다.

이 라이브러리는 네이티브 BigInt 와 정확히 동일하게 동작하는 API를 제공합니다. JSBI를 사용하는 방법은 다음과 같습니다.

 import JSBI from './jsbi.mjs'; const b1 = JSBI.BigInt(Number.MAX_SAFE_INTEGER); const b2 = JSBI.BigInt('10'); const result = JSBI.add(b1, b2); console.log(String(result)); // → '9007199254741001'

JSBI 사용의 장점은 브라우저 지원이 향상되면 코드를 다시 작성할 필요가 없다는 것입니다. 대신 babel 플러그인을 사용하여 JSBI 코드를 기본 BigInt 코드로 자동 컴파일할 수 있습니다. 또한 JSBI의 성능은 기본 BigInt 구현과 동등합니다. 곧 BigInt 에 대한 광범위한 브라우저 지원을 기대할 수 있습니다.

결론

BigInt 는 정수 값이 Number 데이터 유형이 지원하는 범위보다 클 때 사용하기 위한 새로운 데이터 유형입니다. 이 데이터 유형을 사용하면 라이브러리를 사용할 필요 없이 큰 정수에 대한 산술 연산을 안전하게 수행하고, 고해상도 타임스탬프를 나타내고, 큰 정수 ID를 사용하는 등의 작업을 수행할 수 있습니다.

NumberBigInt 피연산자를 혼합하여 산술 연산을 수행할 수 없다는 점을 명심하는 것이 중요합니다. 피연산자 중 하나를 명시적으로 변환하여 작업을 수행해야 하는 도메인을 결정해야 합니다. 또한 호환성을 위해 BigInt 에서 단항 더하기( + ) 연산자를 사용할 수 없습니다.

어떻게 생각해? BigInt 가 유용하다고 생각하십니까? 댓글로 알려주세요!