Українська

Повний посібник з типу BigInt у JavaScript, що розкриває його можливості та застосування. Навчіться обробляти великі цілі числа та виконувати точні обчислення.

JavaScript BigInt: Освоєння арифметики великих цілих чисел

JavaScript, хоч і є універсальною мовою, має обмеження при роботі з дуже великими цілими числами. Стандартний тип `Number` може точно представляти цілі числа лише до певного ліміту, відомого як `Number.MAX_SAFE_INTEGER`. За межами цього ліміту обчислення стають неточними, що призводить до неочікуваних результатів. Саме тут на допомогу приходить BigInt. Представлений в ECMAScript 2020, BigInt — це вбудований об'єкт, який надає спосіб представлення та маніпулювання цілими числами довільного розміру, перевищуючи обмеження стандартного типу `Number`.

Розуміння потреби в BigInt

До появи BigInt розробникам JavaScript доводилося покладатися на бібліотеки або власні реалізації для обробки обчислень з великими цілими числами. Ці рішення часто супроводжувалися зниженням продуктивності та підвищеною складністю. Впровадження BigInt надало нативний та ефективний спосіб роботи з великими цілими числами, відкриваючи можливості для застосувань у різних сферах, зокрема:

Створення значень BigInt

Існує два основних способи створення значень BigInt у JavaScript:

  1. Використання конструктора `BigInt()`: Цей конструктор може перетворити число, рядок або логічне значення на BigInt.
  2. Використання суфікса `n`: Додавання `n` до цілочисельного літералу створює BigInt.

Приклади:

Використання конструктора `BigInt()`:


const bigIntFromNumber = BigInt(12345678901234567890);
const bigIntFromString = BigInt("98765432109876543210");
const bigIntFromBoolean = BigInt(true); // Результат: 1n
const bigIntFromFalseBoolean = BigInt(false); // Результат: 0n

console.log(bigIntFromNumber); // Вивід: 12345678901234567890n
console.log(bigIntFromString); // Вивід: 98765432109876543210n
console.log(bigIntFromBoolean); // Вивід: 1n
console.log(bigIntFromFalseBoolean); // Вивід: 0n

Використання суфікса `n`:


const bigIntLiteral = 12345678901234567890n;
console.log(bigIntLiteral); // Вивід: 12345678901234567890n

Важливе зауваження: Ви не можете безпосередньо змішувати значення BigInt та Number в арифметичних операціях. Вам потрібно явно перетворити їх до одного типу перед виконанням обчислень. Спроба змішати їх напряму призведе до `TypeError`.

Арифметичні операції з BigInt

BigInt підтримує більшість стандартних арифметичних операторів, зокрема:

Приклади:


const a = 12345678901234567890n;
const b = 98765432109876543210n;

const sum = a + b;
const difference = a - b;
const product = a * b;
const quotient = a / 2n; // Примітка: Ділення округлює в бік нуля
const remainder = a % 7n;
const power = a ** 3n; // Піднесення до степеня працює очікувано

console.log("Сума:", sum); // Вивід: Сума: 111111111011111111100n
console.log("Різниця:", difference); // Вивід: Різниця: -86419753208641975320n
console.log("Добуток:", product); // Вивід: Добуток: 1219326311370217957951669538098765432100n
console.log("Частка:", quotient); // Вивід: Частка: 6172839450617283945n
console.log("Залишок:", remainder); // Вивід: Залишок: 5n
console.log("Степінь:", power); // Вивід: Степінь: 187641281029182300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000n

Важливі аспекти:

Оператори порівняння

Ви можете використовувати стандартні оператори порівняння (`==`, `!=`, `<`, `>`, `<=`, `>=`) для порівняння значень BigInt з іншими значеннями BigInt або навіть зі значеннями Number. Однак будьте уважні щодо потенційного приведення типів.

Приклади:


const a = 10n;
const b = 20n;
const c = 10;

console.log(a == b);   // Вивід: false
console.log(a != b);   // Вивід: true
console.log(a < b);    // Вивід: true
console.log(a > b);    // Вивід: false
console.log(a <= b);   // Вивід: true
console.log(a >= b);   // Вивід: false

console.log(a == c);   // Вивід: true (приведення типів)
console.log(a === c);  // Вивід: false (без приведення типів)

Найкраща практика: Використовуйте сувору рівність (`===`) та сувору нерівність (`!==`), щоб уникнути неочікуваного приведення типів при порівнянні значень BigInt та Number.

Перетворення між BigInt та Number

Хоча прямі арифметичні операції між BigInt та Number заборонені, ви можете конвертувати значення між цими двома типами. Однак пам'ятайте про потенційну втрату точності при перетворенні BigInt на Number, якщо значення BigInt перевищує `Number.MAX_SAFE_INTEGER`.

Приклади:


const bigIntValue = 9007199254740991n; // Number.MAX_SAFE_INTEGER
const numberValue = Number(bigIntValue); // Перетворення BigInt на Number
console.log(numberValue); // Вивід: 9007199254740991

const largerBigIntValue = 9007199254740992n; // Перевищує Number.MAX_SAFE_INTEGER
const largerNumberValue = Number(largerBigIntValue);
console.log(largerNumberValue); // Вивід: 9007199254740992 (може бути неточним)

const numberToBigInt = BigInt(12345); // Перетворення Number на BigInt
console.log(numberToBigInt); // Вивід: 12345n

Сценарії використання та приклади

Криптографія

Криптографічні алгоритми часто покладаються на дуже великі прості числа для забезпечення безпеки. BigInt надає спосіб ефективно представляти ці числа та маніпулювати ними.


// Приклад: Генерація простої (небезпечної) пари ключів
function generateKeyPair() {
  const p = 281n; // Просте число
  const q = 283n; // Інше просте число
  const n = p * q; // Модуль
  const totient = (p - 1n) * (q - 1n); // Функція Ейлера

  // Вибираємо e (відкриту експоненту) так, що 1 < e < totient і НСД(e, totient) = 1
  const e = 17n;

  // Обчислюємо d (секретну експоненту) так, що (d * e) % totient = 1
  let d = 0n;
  for (let i = 1n; i < totient; i++) {
    if ((i * e) % totient === 1n) {
      d = i;
      break;
    }
  }

  return {
    publicKey: { n, e },
    privateKey: { n, d },
  };
}

const keyPair = generateKeyPair();
console.log("Відкритий ключ:", keyPair.publicKey);
console.log("Секретний ключ:", keyPair.privateKey);

Примітка: Це спрощений приклад лише для демонстраційних цілей. Реальна криптографія використовує набагато більші прості числа та складніші алгоритми.

Фінансові розрахунки

При роботі з великими сумами грошей, особливо в міжнародних транзакціях, точність є критично важливою. BigInt може запобігти помилкам округлення та забезпечити точні обчислення.


// Приклад: Розрахунок складних відсотків
function calculateCompoundInterest(principal, rate, time) {
  const principalBigInt = BigInt(principal * 100); // Переводимо в центи
  const rateBigInt = BigInt(rate * 10000);       // Переводимо в десятитисячні відсотка
  const timeBigInt = BigInt(time);

  let amount = principalBigInt;
  for (let i = 0n; i < timeBigInt; i++) {
    amount = amount * (10000n + rateBigInt) / 10000n;
  }

  const amountInDollars = Number(amount) / 100;
  return amountInDollars;
}

const principal = 1000000; // $1,000,000
const rate = 0.05;    // 5% відсоткова ставка
const time = 10;     // 10 років

const finalAmount = calculateCompoundInterest(principal, rate, time);
console.log("Кінцева сума:", finalAmount); // Вивід: Кінцева сума: 1628894.6267774413 (приблизно)

У цьому прикладі ми перетворюємо основну суму та ставку на значення BigInt, щоб уникнути помилок округлення під час обчислення. Потім результат конвертується назад у Number для відображення.

Робота з великими ID

У розподілених системах генерація унікальних ID на кількох серверах може бути складною. Використання BigInt дозволяє створювати дуже великі ID, ймовірність колізії яких є низькою.


// Приклад: Генерація унікального ID на основі часової мітки та ID сервера
function generateUniqueId(serverId) {
  const timestamp = BigInt(Date.now());
  const serverIdBigInt = BigInt(serverId);
  const random = BigInt(Math.floor(Math.random() * 1000)); // Додаємо трохи випадковості

  // Комбінуємо значення для створення унікального ID
  const uniqueId = (timestamp << 20n) + (serverIdBigInt << 10n) + random;
  return uniqueId.toString(); // Повертаємо у вигляді рядка для зручності обробки
}

const serverId = 123; // Приклад ID сервера
const id1 = generateUniqueId(serverId);
const id2 = generateUniqueId(serverId);

console.log("Унікальний ID 1:", id1);
console.log("Унікальний ID 2:", id2);

BigInt та JSON

Значення BigInt не підтримуються в JSON нативно. Спроба серіалізувати об'єкт JavaScript, що містить BigInt, за допомогою `JSON.stringify()` призведе до `TypeError`. Для обробки значень BigInt при роботі з JSON у вас є кілька варіантів:

  1. Перетворення на рядок: Перетворіть BigInt на рядок перед серіалізацією. Це найпоширеніший і найпростіший підхід.
  2. Власна серіалізація/десеріалізація: Використовуйте власну функцію серіалізації/десеріалізації для обробки значень BigInt.

Приклади:

Перетворення на рядок:


const data = {
  id: 12345678901234567890n,
  name: "Example Data",
};

// Перетворюємо BigInt на рядок перед серіалізацією
data.id = data.id.toString();

const jsonData = JSON.stringify(data);
console.log(jsonData); // Вивід: {"id":"12345678901234567890","name":"Example Data"}

// При десеріалізації вам потрібно буде перетворити рядок назад на BigInt
const parsedData = JSON.parse(jsonData, (key, value) => {
  if (key === "id") {
    return BigInt(value);
  }
  return value;
});

console.log(parsedData.id); // Вивід: 12345678901234567890n

Власна серіалізація/десеріалізація (з використанням `replacer` та `reviver`):


const data = {
  id: 12345678901234567890n,
  name: "Example Data",
};

// Власна серіалізація
const jsonData = JSON.stringify(data, (key, value) => {
  if (typeof value === 'bigint') {
    return value.toString();
  } else {
    return value;
  }
});

console.log(jsonData);

// Власна десеріалізація
const parsedData = JSON.parse(jsonData, (key, value) => {
    if (typeof value === 'string' && /^[0-9]+$/.test(value)) { // перевіряємо, чи це рядок, що складається з цифр
      try {
        return BigInt(value);
      } catch(e) {
          return value;
      }
    }
    return value;
});

console.log(parsedData.id);

Сумісність з браузерами

BigInt широко підтримується в сучасних браузерах. Однак важливо перевіряти сумісність для старих браузерів або середовищ. Ви можете використовувати інструмент, такий як Can I use, для перевірки підтримки браузерами. Якщо вам потрібно підтримувати старі браузери, ви можете розглянути використання поліфіла, але пам'ятайте, що поліфіли можуть впливати на продуктивність.

Аспекти продуктивності

Хоча BigInt надає потужний інструмент для роботи з великими цілими числами, важливо пам'ятати про можливі наслідки для продуктивності.

Тому використовуйте BigInt лише за необхідності та оптимізуйте свій код для підвищення продуктивності, якщо ви виконуєте велику кількість операцій з BigInt.

Висновок

BigInt — це цінне доповнення до JavaScript, що дозволяє розробникам обробляти арифметику великих цілих чисел з високою точністю. Розуміючи його можливості, обмеження та сценарії використання, ви можете застосовувати BigInt для створення надійних і точних додатків у різних сферах, включаючи криптографію, фінансові розрахунки та наукові обчислення. Не забувайте враховувати сумісність з браузерами та аспекти продуктивності при використанні BigInt у своїх проектах.

Подальше вивчення

Цей посібник надає всебічний огляд BigInt у JavaScript. Досліджуйте пов'язані ресурси для отримання більш детальної інформації та вивчення передових технік.