BigInt と Number を安全に混ぜるための小さなライブラリ
/**
* BigNumSafe.js
* Number と BigInt を安全に混ぜるためのユーティリティ関数集
* - 自動変換ルールつき
* - 桁あふれや丸め誤差を防止
* - 型エラーを自動検出
*/
//----------------------------------------------------------
// 1️⃣ 型チェックと安全変換
//----------------------------------------------------------
/**
* 値を安全に BigInt に変換(整数 Number のみ許可)
*/
export function toBigIntSafe(value) {
if (typeof value === "bigint") return value;
if (typeof value === "number") {
if (!Number.isInteger(value)) {
throw new TypeError(`toBigIntSafe: 整数でない値 (${value}) は変換できません`);
}
return BigInt(value);
}
if (typeof value === "string" && /^[+-]?\d+$/.test(value)) {
return BigInt(value);
}
throw new TypeError(`toBigIntSafe: 変換できない型 (${typeof value})`);
}
/**
* 値を安全に Number に変換(安全範囲外は警告)
*/
export function toNumberSafe(value) {
if (typeof value === "number") return value;
if (typeof value === "bigint") {
const num = Number(value);
if (Math.abs(num) > Number.MAX_SAFE_INTEGER) {
console.warn(`⚠️ 精度が失われる可能性があります: ${value}n`);
}
return num;
}
throw new TypeError(`toNumberSafe: 変換できない型 (${typeof value})`);
}
//----------------------------------------------------------
// 2️⃣ 自動演算(Number/BigInt 混在を安全に扱う)
//----------------------------------------------------------
/**
* ルール:
* - どちらかが BigInt なら BigInt 計算
* - 両方 Number なら Number 計算
*/
function normalizeType(a, b) {
const hasBig = typeof a === "bigint" || typeof b === "bigint";
return hasBig
? [toBigIntSafe(a), toBigIntSafe(b), "bigint"]
: [a, b, "number"];
}
export const SafeMath = {
add(a, b) {
const [x, y, type] = normalizeType(a, b);
return type === "bigint" ? x + y : x + y;
},
sub(a, b) {
const [x, y, type] = normalizeType(a, b);
return type === "bigint" ? x - y : x - y;
},
mul(a, b) {
const [x, y, type] = normalizeType(a, b);
return type === "bigint" ? x * y : x * y;
},
div(a, b) {
const [x, y, type] = normalizeType(a, b);
if (y === 0n || y === 0) throw new Error("0で割ることはできません");
// BigInt の場合は整数除算
return type === "bigint" ? x / y : x / y;
},
mod(a, b) {
const [x, y, type] = normalizeType(a, b);
return type === "bigint" ? x % y : x % y;
},
pow(a, b) {
const [x, y, type] = normalizeType(a, b);
return type === "bigint" ? x ** y : x ** y;
}
};
//----------------------------------------------------------
// 3️⃣ 比較系(型が違っても比較できる)
//----------------------------------------------------------
export const SafeCompare = {
eq(a, b) {
const [x, y] = normalizeType(a, b);
return x === y;
},
lt(a, b) {
const [x, y] = normalizeType(a, b);
return x < y;
},
gt(a, b) {
const [x, y] = normalizeType(a, b);
return x > y;
},
le(a, b) {
const [x, y] = normalizeType(a, b);
return x <= y;
},
ge(a, b) {
const [x, y] = normalizeType(a, b);
return x >= y;
}
};
//----------------------------------------------------------
// 4️⃣ 型を意識しない共通API(便利ショートカット)
//----------------------------------------------------------
export function auto(a, b, op) {
const ops = {
"+": SafeMath.add,
"-": SafeMath.sub,
"*": SafeMath.mul,
"/": SafeMath.div,
"%": SafeMath.mod,
"**": SafeMath.pow
};
const fn = ops[op];
if (!fn) throw new Error(`未対応の演算子: ${op}`);
return fn(a, b);
}
JavaScript使い方例
import { SafeMath, SafeCompare, auto } from "./BigNumSafe.js";
// ✅ 型混在でも自動で安全に処理
console.log(SafeMath.add(10n, 5)); // → 15n
console.log(SafeMath.div(7n, 3)); // → 2n(整数除算)
console.log(SafeMath.div(7, 3)); // → 2.3333333333333335(Number)
console.log(SafeCompare.eq(54, 54n)); // → true
console.log(SafeCompare.lt(2n, 5)); // → true
// ✅ auto() を使えば演算子文字列で処理できる
console.log(auto(2n, 3, "**")); // → 8n
console.log(auto(2, 3, "**")); // → 8
// ⚠️ 浮動小数を BigInt に混ぜるとエラー
try {
SafeMath.add(1.5, 2n);
} catch (e) {
console.error(e.message);
}
JavaScript自動変換ルールまとめ
| 条件 | 変換先 | 備考 |
|---|---|---|
| 両方 Number | Number 計算 | 小数含めOK |
| 片方 BigInt | 両方 BigInt に変換 | 整数除算になる |
| 小数を BigInt に混ぜた | エラー | 丸め禁止 |
比較系(==, <, >) | 自動で同じ型に揃える | 精度は保たれる |
| 結果が安全範囲外(Number) | 警告を出す | 精度落ちに注意 |
特徴
✅ 自動で安全な型変換
✅ 小数の混入を検出
✅ BigInt 演算の整数除算を維持
✅ 精度ロスを警告
✅ Node.js/ブラウザ両対応

