JavaScript | 長整数リテラルを数値に型変換

JavaScript JavaScript
スポンサーリンク

BigInt と Number を安全に混ぜて使うためのユーティリティライブラリ(高機能版)

目的

JavaScript では

10n + 5  // ❌ TypeError: Cannot mix BigInt and other types
JavaScript

のように、BigIntNumber を直接混ぜて計算できません。

このユーティリティは:

  • 型を自動でそろえる
  • 精度が失われる可能性を検出・警告
  • 戻り型(BigInt or Number)を選べる
  • ログ出力やフォールバック設定も可能

という「安全で柔軟な変換&演算レイヤー」を提供します。

コード全体

// bigint-utils.js
const BigNumUtils = (() => {

  // ====== 設定 ======
  const DEFAULT_OPTIONS = {
    prefer: "bigint",     // 戻り型の優先: "bigint" | "number" | "auto"
    log: false,            // 自動ログ出力するか
    fallback: "error",     // 精度喪失時の動作: "error" | "warn" | "truncate"
  };

  const MAX_SAFE = BigInt(Number.MAX_SAFE_INTEGER);
  const MIN_SAFE = BigInt(Number.MIN_SAFE_INTEGER);

  // ====== 内部関数 ======
  function logIfNeeded(opts, msg, level = "info") {
    if (!opts.log) return;
    const tag = level === "warn" ? "⚠️" : "ℹ️";
    console.log(`${tag} [BigNumUtils] ${msg}`);
  }

  function isSafeBigInt(bi) {
    return bi <= MAX_SAFE && bi >= MIN_SAFE;
  }

  function toBigIntSafe(value, opts = DEFAULT_OPTIONS) {
    if (typeof value === "bigint") return value;
    if (typeof value === "number") {
      if (!Number.isFinite(value)) throw new TypeError("Infinity や NaN は変換できません");
      if (!Number.isInteger(value)) throw new RangeError("小数は BigInt に変換できません");
      return BigInt(value);
    }
    throw new TypeError(`BigInt に変換できない型: ${typeof value}`);
  }

  function toNumberSafe(value, opts = DEFAULT_OPTIONS) {
    if (typeof value === "number") return value;
    if (typeof value === "bigint") {
      if (!isSafeBigInt(value)) {
        const msg = `Number の安全範囲 (${Number.MIN_SAFE_INTEGER}${Number.MAX_SAFE_INTEGER}) を超えています`;
        if (opts.fallback === "error") throw new RangeError(msg);
        if (opts.fallback === "warn") logIfNeeded(opts, msg, "warn");
      }
      return Number(value);
    }
    throw new TypeError(`Number に変換できない型: ${typeof value}`);
  }

  function unify(a, b, opts = DEFAULT_OPTIONS) {
    const prefer = opts.prefer;
    if (typeof a === typeof b) return [a, b];

    // どちらか bigint どちらか number の場合
    if (prefer === "bigint") {
      return [toBigIntSafe(a, opts), toBigIntSafe(b, opts)];
    } else if (prefer === "number") {
      return [toNumberSafe(a, opts), toNumberSafe(b, opts)];
    } else {
      // auto: 安全なら number に、危険なら bigint に寄せる
      if (typeof a === "bigint" && !isSafeBigInt(a)) return [a, toBigIntSafe(b)];
      if (typeof b === "bigint" && !isSafeBigInt(b)) return [toBigIntSafe(a), b];
      return [toNumberSafe(a), toNumberSafe(b)];
    }
  }

  // ====== 公開API ======

  return {
    config(customOpts = {}) {
      return { ...DEFAULT_OPTIONS, ...customOpts };
    },

    add(a, b, opts = DEFAULT_OPTIONS) {
      const [x, y] = unify(a, b, opts);
      const result = x + y;
      logIfNeeded(opts, `add(${a}, ${b}) = ${result}`);
      return result;
    },

    sub(a, b, opts = DEFAULT_OPTIONS) {
      const [x, y] = unify(a, b, opts);
      const result = x - y;
      logIfNeeded(opts, `sub(${a}, ${b}) = ${result}`);
      return result;
    },

    mul(a, b, opts = DEFAULT_OPTIONS) {
      const [x, y] = unify(a, b, opts);
      const result = x * y;
      logIfNeeded(opts, `mul(${a}, ${b}) = ${result}`);
      return result;
    },

    div(a, b, opts = DEFAULT_OPTIONS) {
      const [x, y] = unify(a, b, opts);
      if (y === 0n || y === 0) throw new RangeError("0 で割り算できません");
      const result = x / y;
      logIfNeeded(opts, `div(${a}, ${b}) = ${result}`);
      return result;
    },

    toBigIntSafe,
    toNumberSafe,
    unify,
  };
})();
JavaScript

使い方例

例1:BigInt を優先して混合計算

const opts = BigNumUtils.config({ prefer: "bigint", log: true });

console.log(BigNumUtils.add(10n, 5, opts));   // → 15n(BigInt)
console.log(BigNumUtils.mul(9, 4n, opts));   // → 36n(BigInt)
JavaScript

ログ出力例:

ℹ️ [BigNumUtils] add(10n, 5) = 15n
ℹ️ [BigNumUtils] mul(9, 4n) = 36n
JavaScript

例2:Number 優先で統一

const opts = BigNumUtils.config({ prefer: "number" });

console.log(BigNumUtils.add(10n, 5, opts));  // → 15
JavaScript

例3:auto(安全なら Number、危険なら BigInt)

const opts = BigNumUtils.config({ prefer: "auto", log: true });

const big = 9999999999999999999999999n;
console.log(BigNumUtils.add(big, 1, opts)); // → BigInt に統一(安全範囲超え)
JavaScript

例4:フォールバック戦略

const opts = BigNumUtils.config({
  prefer: "number",
  fallback: "warn",
  log: true
});

const big = 99999999999999999999n;
console.log(BigNumUtils.toNumberSafe(big, opts)); 
// ⚠️ 警告を出しつつ変換(精度が失われる可能性あり)
JavaScript

機能まとめ

機能説明
preferbigint / number / auto のどれを優先するか
fallback精度超過時にどうするか:
error(例外) / warn(警告出力) / truncate(そのまま切り捨て)
logtrue にすると内部処理ログを出力
toBigIntSafe() / toNumberSafe()個別変換関数(安全チェック付き)
unify()混在した型を統一して返す内部関数
add() / sub() / mul() / div()自動変換付き算術演算

発展アイデア

もし次のステップに進めたいなら:

  1. 小数を自動的に切り捨て/四捨五入 して BigInt に変換できるモード
  2. BigDecimal(任意精度小数)風の実装 に拡張
  3. TypeScript 型定義 (.d.ts) 付きバージョン
  4. ブラウザ+Node.js 両対応のミニライブラリとして公開(npm対応)
タイトルとURLをコピーしました