日本語

JavaScriptのBigInt型に関する包括的なガイド。その機能、使い方、巨大整数演算の応用例を解説します。JavaScriptの制限を克服し、高精度で複雑な計算を実行する方法を学びましょう。

JavaScript BigInt: 巨大整数演算をマスターする

JavaScriptは多機能な言語ですが、非常に大きな整数を扱う際には制限があります。標準のNumber型は、Number.MAX_SAFE_INTEGERとして知られる特定の上限までしか整数を正確に表現できません。この上限を超えると計算が不正確になり、予期せぬ結果につながります。ここで救世主となるのがBigIntです。ECMAScript 2020で導入されたBigIntは、標準のNumber型の制限を超える、任意のサイズの整数を表現し操作する方法を提供する組み込みオブジェクトです。

BigIntが必要とされる理由

BigIntが登場する前、JavaScript開発者は巨大な整数計算を扱うためにライブラリやカスタム実装に頼らなければなりませんでした。これらの解決策はしばしばパフォーマンスのオーバーヘッドや複雑性の増加を伴いました。BigIntの導入により、巨大な整数を扱うためのネイティブで効率的な方法が提供され、以下を含む様々な分野での応用が可能になりました:

BigInt値の作成

JavaScriptでBigInt値を作成するには、主に2つの方法があります:

  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

重要な注意点: 算術演算においてBigIntNumberの値を直接混在させることはできません。 計算を行う前に、それらを明示的に同じ型に変換する必要があります。 直接混在させようとすると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:", sum); // Output: Sum: 111111111011111111100n
console.log("Difference:", difference); // Output: Difference: -86419753208641975320n
console.log("Product:", product); // Output: Product: 1219326311370217957951669538098765432100n
console.log("Quotient:", quotient); // Output: Quotient: 6172839450617283945n
console.log("Remainder:", remainder); // Output: Remainder: 5n
console.log("Power:", power); // Output: 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 (型強制なし)

ベストプラクティス: BigIntNumberの値を比較する際は、予期しない型強制を避けるために、厳密等価 (===) および厳密不等価 (!==) を使用してください。

BigIntとNumber間の変換

BigIntNumber間の直接的な算術演算は許可されていませんが、2つの型の間で変換することは可能です。ただし、BigInt値がNumber.MAX_SAFE_INTEGERを超える場合にBigIntNumberに変換すると、精度が失われる可能性があることに注意してください。

例:


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 かつ gcd(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("Public Key:", keyPair.publicKey);
console.log("Private Key:", keyPair.privateKey);

注意: これはデモンストレーション目的の単純化された例です。実際の暗号技術では、はるかに大きな素数とより洗練されたアルゴリズムが使用されます。

金融計算

特に国際取引で多額の金銭を扱う場合、精度は非常に重要です。BigIntは丸め誤差を防ぎ、正確な計算を保証します。


// 例: 複利計算
function calculateCompoundInterest(principal, rate, time) {
  const principalBigInt = BigInt(principal * 100); // セントに変換
  const rateBigInt = BigInt(rate * 10000);       // 1パーセントの1万分の1に変換
  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("Final Amount:", finalAmount); // Output: Final Amount: 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("Unique ID 1:", id1);
console.log("Unique ID 2:", id2);

BigIntとJSON

BigInt値はJSONでネイティブにサポートされていません。 BigIntを含むJavaScriptオブジェクトをJSON.stringify()でシリアライズしようとすると、TypeErrorが発生します。JSONでBigInt値を扱うには、いくつかの選択肢があります:

  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を使用する際には、ブラウザの互換性とパフォーマンスへの影響を考慮することを忘れないでください。

さらなる探求

このガイドは、JavaScriptにおけるBigIntの包括的な概要を提供しました。より詳細な情報や高度なテクニックについては、リンク先のリソースを参照してください。

JavaScript BigInt: JavaScriptにおける巨大整数演算をマスターする | MLOG