JSON.stringify({ value: 10n }); // ❌ TypeError: Do not know how to serialize a BigInt
JavaScript
対処法:
// ① 文字列に変換するJSON.stringify({ value: 10n.toString() }); // ✅ '{"value":"10"}'// ② BigInt対応のカスタム関数を使うJSON.stringify({ value: 10n }, (_, v) =>typeof v ==="bigint"? v.toString() : v);
JavaScript
11. まとめ
項目
BigInt
Number
内部表現
任意長整数
64ビット浮動小数点
小数の扱い
❌ 不可
✅ 可能
安全な整数範囲
無限大(メモリ制限のみ)
±9,007,199,254,740,991
型
"bigint"
"number"
Math関数
❌ 非対応
✅ 対応
JSON.stringify
❌ 直接不可
✅ OK
用途
暗号、ブロックチェーン、ID生成など
一般的な数値計算
12. ちょっとした裏ワザ
➤ BigIntを扱いやすくするヘルパー関数
consttoBig= (v) =>typeof v ==="bigint"? v :BigInt(v);consttoNum= (v) =>typeof v ==="number"? v :Number(v);
JavaScript
➤ 大きなIDを文字列から安全に扱う
const id =BigInt("123456789012345678901234567890");console.log(id +1n); // ✅ OK
JavaScript
まとめ(初心者が押さえるべきポイント)
n をつけると BigInt(例:123n)
とても大きな整数も正確に扱える
Number と混ぜて計算できない
割り算は小数点以下切り捨て
JSON.stringify や Math 関数は非対応
暗号・ブロックチェーン・高精度IDなどで必須
BigIntを使った安全なID生成ツール
/*BigInt ID Generator- Compact, collision-resistant ID generator based on BigInt (inspired by Snowflake)- Features: - 64+ bit BigInt ID (timestamp | nodeId | sequence | random) - Configurable bit lengths - Monotonic within the same instance (sequence increments for same millisecond) - Encodes to compact string (base62) and decodes back - Safe to use for distributed systems with a nodeIdUsage examples:const gen = new BigIntIDGenerator({ nodeId: 1 });const id = gen.next(); // BigIntconst str = gen.nextString(); // base62 stringconst parsed = BigIntIDGenerator.parse(id); // {timestamp, nodeId, sequence, random}const fromStr = BigIntIDGenerator.fromString(str);*/// --- Utility: base62 encoding/decoding ---const BASE62 ='0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';functionbase62EncodeBigInt(value) {if (value ===0n) return'0';let v = value <0n?-value : value;let out ='';const base =62n;while (v >0n) {const rem = v % base; out = BASE62[Number(rem)] + out; v = v / base; }return value <0n?'-'+ out : out;}functionbase62DecodeToBigInt(str) {if (str ==='0') return0n;let negative =false;let s = str;if (s[0] ==='-') { negative =true; s = s.slice(1); }let out =0n;const base =62n;for (let i =0; i < s.length; i++) {const idx =BigInt(BASE62.indexOf(s[i]));if (idx <0n) thrownewError('Invalid base62 character: '+ s[i]); out = out * base + idx; }return negative ?-out : out;}// --- BigInt ID Generator ---classBigIntIDGenerator {/** * options: * epoch: custom epoch in ms (Number). default = 2020-01-01T00:00:00Z * timestampBits: bits reserved for timestamp * nodeIdBits: bits for node id * sequenceBits: bits for sequence counter * randomBits: bits for appended randomness * nodeId: numeric node id (Number or BigInt). If omitted, randomly chosen within range. */constructor(options= {}) {const { epoch = Date.UTC(2020, 0, 1), // 2020-01-01 timestampBits =42, nodeIdBits =10, sequenceBits =10, randomBits =2, nodeId =null, } = options;// store configthis.epoch =BigInt(epoch);this.timestampBits = timestampBits;this.nodeIdBits = nodeIdBits;this.sequenceBits = sequenceBits;this.randomBits = randomBits;// compute shifts and masksthis.randomMask = (1n<<BigInt(randomBits)) -1n;this.sequenceMask = (1n<<BigInt(sequenceBits)) -1n;this.nodeIdMask = (1n<<BigInt(nodeIdBits)) -1n;this.randomShift =0n;this.sequenceShift =BigInt(randomBits);this.nodeShift =BigInt(randomBits + sequenceBits);this.timestampShift =BigInt(randomBits + sequenceBits + nodeIdBits);// max valuesthis.maxNodeId =Number(this.nodeIdMask);// node id selectionif (nodeId ===null|| nodeId ===undefined) {// choose a pseudo-random node id in [0, maxNodeId]this.nodeId =BigInt(Math.floor(Math.random() * (this.maxNodeId +1))); } else {const n =BigInt(nodeId);if (n <0n|| n >this.nodeIdMask) thrownewRangeError(`nodeId out of range (0..${this.maxNodeId})`);this.nodeId = n; }// runtime state for monotonic sequencethis.lastTs =-1n; // last timestamp (ms) seenthis.sequence =0n; // current sequence within same ms }// --- helpers ---_nowMsBigInt() {returnBigInt(Date.now()); }_waitNextMs(currentTs) {// busy-wait until Date.now() advances. Avoids returning duplicate timestamp.// Only used when sequence overflows within same ms.let ts =this._nowMsBigInt();while (ts <= currentTs) { ts =this._nowMsBigInt(); }return ts; }/** produce next ID as BigInt */next() {const now =this._nowMsBigInt();const ts = now -this.epoch;if (ts <0n) thrownewError('Clock before epoch');if (now ===this.lastTs) {// same millisecond -> increment sequencethis.sequence = (this.sequence +1n) &this.sequenceMask;if (this.sequence ===0n) {// overflowed sequence within same ms -> wait for next msconst nextTs =this._waitNextMs(now);returnthis._buildId(nextTs -this.epoch, 0n); } } else {// new millisecond -> reset sequence to random small value to reduce collisionsthis.sequence =BigInt(Math.floor(Math.random() *Number(this.sequenceMask +1n)));this.lastTs = now; }returnthis._buildId(ts, this.sequence); }_randomBits() {if (this.randomMask ===0n) return0n;returnBigInt(Math.floor(Math.random() *Number(this.randomMask +1n))); }_buildId(ts, sequence) {const tsPart = (BigInt(ts) & ((1n<<BigInt(this.timestampBits)) -1n)) <<this.timestampShift;const nodePart = (BigInt(this.nodeId) &this.nodeIdMask) <<this.nodeShift;const seqPart = (BigInt(sequence) &this.sequenceMask) <<this.sequenceShift;const randPart = (this._randomBits() &this.randomMask) <<this.randomShift;const id = tsPart | nodePart | seqPart | randPart;return id; }/** produce next ID as base62 string */nextString() {returnbase62EncodeBigInt(this.next()); }/** Encode arbitrary BigInt id to base62 string */statictoString(id) {returnbase62EncodeBigInt(BigInt(id)); }/** Decode base62 string back to BigInt id */staticfromString(str) {returnbase62DecodeToBigInt(str); }/** Parse components from an ID (BigInt or base62 string) */parseId(value) {let id =typeof value ==='string'?base62DecodeToBigInt(value) :BigInt(value);return BigIntIDGenerator.parseWithConfig(id, this); }/** Static parse given an id and optionally a generator/config */staticparse(idLike, configLike) {const id =typeof idLike ==='string'?base62DecodeToBigInt(idLike) :BigInt(idLike);if (configLike instanceofBigIntIDGenerator) {return BigIntIDGenerator.parseWithConfig(id, configLike); }// if config not provided, assume defaults used in generator constructorconst temp =newBigIntIDGenerator(configLike || {});return BigIntIDGenerator.parseWithConfig(id, temp); }staticparseWithConfig(id, config) {const ts = (id >> config.timestampShift);const node = (id >> config.nodeShift) & config.nodeIdMask;const seq = (id >> config.sequenceShift) & config.sequenceMask;const rand = (id >> config.randomShift) & config.randomMask;return { id: id, timestamp: Number(ts + config.epoch), // in ms as Number (may lose precision for extremely large epochs) timestampBigInt: ts + config.epoch, elapsedSinceEpoch: Number(ts), nodeId: node, sequence: seq, random: rand, config: { epoch: config.epoch, timestampBits: config.timestampBits, nodeIdBits: config.nodeIdBits, sequenceBits: config.sequenceBits, randomBits: config.randomBits, } }; }}// Export for Node and ESMif (typeofmodule!=='undefined'&&module.exports) {module.exports= BigIntIDGenerator;} else { window.BigIntIDGenerator = BigIntIDGenerator;}// --- Self-test / example (only runs in Node when this file executed directly) ---if (typeof require !=='undefined'&& require.main ===module) { (async () => {const gen =newBigIntIDGenerator({ nodeId: 1 }); console.log('nodeId:', gen.nodeId.toString());const ids = [];for (let i =0; i <5; i++) {const id = gen.next();const s = gen.nextString(); ids.push({ id, s }); console.log('BigInt:', id.toString()); console.log('base62:', s); console.log('parsed:', gen.parseId(id)); }// test decodeconst decoded = BigIntIDGenerator.fromString(ids[0].s); console.log('decoded equals original:', decoded === ids[0].id); })();}
JavaScript
BigIntとNumberを自動で変換するヘルパー関数
/*BigInt ID Generator + Conversion Helpers*/// --- Utility: base62 encoding/decoding ---const BASE62 ='0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';functionbase62EncodeBigInt(value) {if (value ===0n) return'0';let v = value <0n?-value : value;let out ='';const base =62n;while (v >0n) {const rem = v % base; out = BASE62[Number(rem)] + out; v = v / base; }return value <0n?'-'+ out : out;}functionbase62DecodeToBigInt(str) {if (str ==='0') return0n;let negative =false;let s = str;if (s[0] ==='-') { negative =true; s = s.slice(1); }let out =0n;const base =62n;for (let i =0; i < s.length; i++) {const idx =BigInt(BASE62.indexOf(s[i]));if (idx <0n) thrownewError('Invalid base62 character: '+ s[i]); out = out * base + idx; }return negative ?-out : out;}// --- Helper: BigInt <-> Number auto conversion ---const BigIntHelper = {/** Safely converts to Number (with overflow detection) */toNumber(value) {if (typeof value ==='bigint') {const num =Number(value);if (BigInt(num) !== value) {thrownewRangeError('BigInt value too large to fit in Number'); }return num; }if (typeof value ==='number') return value;thrownewTypeError('Expected Number or BigInt'); },/** Converts to BigInt safely */toBigInt(value) {if (typeof value ==='bigint') return value;if (typeof value ==='number') {if (!Number.isFinite(value) ||!Number.isSafeInteger(value)) {thrownewRangeError('Cannot convert unsafe or non-finite number to BigInt'); }returnBigInt(value); }if (typeof value ==='string') {if (/^-?\d+$/.test(value)) returnBigInt(value);thrownewTypeError('Invalid string for BigInt conversion'); }thrownewTypeError('Unsupported type for BigInt conversion'); },/** Automatically handles mixed-type arithmetic safely */add(a, b) {if (typeof a ==='bigint'||typeof b ==='bigint') returnthis.toBigInt(a) +this.toBigInt(b);return a + b; },subtract(a, b) {if (typeof a ==='bigint'||typeof b ==='bigint') returnthis.toBigInt(a) -this.toBigInt(b);return a - b; },multiply(a, b) {if (typeof a ==='bigint'||typeof b ==='bigint') returnthis.toBigInt(a) *this.toBigInt(b);return a * b; },divide(a, b) {if (typeof a ==='bigint'||typeof b ==='bigint') returnthis.toBigInt(a) /this.toBigInt(b);return a / b; }};// --- BigInt ID Generator ---classBigIntIDGenerator {constructor(options= {}) {const { epoch = Date.UTC(2020, 0, 1), timestampBits =42, nodeIdBits =10, sequenceBits =10, randomBits =2, nodeId =null, } = options;this.epoch =BigInt(epoch);this.timestampBits = timestampBits;this.nodeIdBits = nodeIdBits;this.sequenceBits = sequenceBits;this.randomBits = randomBits;this.randomMask = (1n<<BigInt(randomBits)) -1n;this.sequenceMask = (1n<<BigInt(sequenceBits)) -1n;this.nodeIdMask = (1n<<BigInt(nodeIdBits)) -1n;this.randomShift =0n;this.sequenceShift =BigInt(randomBits);this.nodeShift =BigInt(randomBits + sequenceBits);this.timestampShift =BigInt(randomBits + sequenceBits + nodeIdBits);this.maxNodeId =Number(this.nodeIdMask);if (nodeId ===null|| nodeId ===undefined) {this.nodeId =BigInt(Math.floor(Math.random() * (this.maxNodeId +1))); } else {const n =BigInt(nodeId);if (n <0n|| n >this.nodeIdMask) thrownewRangeError(`nodeId out of range (0..${this.maxNodeId})`);this.nodeId = n; }this.lastTs =-1n;this.sequence =0n; }_nowMsBigInt() { returnBigInt(Date.now()); }_waitNextMs(currentTs) {let ts =this._nowMsBigInt();while (ts <= currentTs) ts =this._nowMsBigInt();return ts; }next() {const now =this._nowMsBigInt();const ts = now -this.epoch;if (ts <0n) thrownewError('Clock before epoch');if (now ===this.lastTs) {this.sequence = (this.sequence +1n) &this.sequenceMask;if (this.sequence ===0n) {const nextTs =this._waitNextMs(now);returnthis._buildId(nextTs -this.epoch, 0n); } } else {this.sequence =BigInt(Math.floor(Math.random() *Number(this.sequenceMask +1n)));this.lastTs = now; }returnthis._buildId(ts, this.sequence); }_randomBits() {if (this.randomMask ===0n) return0n;returnBigInt(Math.floor(Math.random() *Number(this.randomMask +1n))); }_buildId(ts, sequence) {const tsPart = (BigInt(ts) & ((1n<<BigInt(this.timestampBits)) -1n)) <<this.timestampShift;const nodePart = (BigInt(this.nodeId) &this.nodeIdMask) <<this.nodeShift;const seqPart = (BigInt(sequence) &this.sequenceMask) <<this.sequenceShift;const randPart = (this._randomBits() &this.randomMask) <<this.randomShift;return tsPart | nodePart | seqPart | randPart; }nextString() { returnbase62EncodeBigInt(this.next()); }statictoString(id) { returnbase62EncodeBigInt(BigInt(id)); }staticfromString(str) { returnbase62DecodeToBigInt(str); }parseId(value) {let id =typeof value ==='string'?base62DecodeToBigInt(value) :BigInt(value);return BigIntIDGenerator.parseWithConfig(id, this); }staticparse(idLike, configLike) {const id =typeof idLike ==='string'?base62DecodeToBigInt(idLike) :BigInt(idLike);if (configLike instanceofBigIntIDGenerator) return BigIntIDGenerator.parseWithConfig(id, configLike);const temp =newBigIntIDGenerator(configLike || {});return BigIntIDGenerator.parseWithConfig(id, temp); }staticparseWithConfig(id, config) {const ts = (id >> config.timestampShift);const node = (id >> config.nodeShift) & config.nodeIdMask;const seq = (id >> config.sequenceShift) & config.sequenceMask;const rand = (id >> config.randomShift) & config.randomMask;return { id, timestamp: BigIntHelper.toNumber(ts + config.epoch), timestampBigInt: ts + config.epoch, elapsedSinceEpoch: BigIntHelper.toNumber(ts), nodeId: node, sequence: seq, random: rand, config: { epoch: config.epoch, timestampBits: config.timestampBits, nodeIdBits: config.nodeIdBits, sequenceBits: config.sequenceBits, randomBits: config.randomBits, } }; }}if (typeofmodule!=='undefined'&&module.exports) {module.exports= { BigIntIDGenerator, BigIntHelper };} else { window.BigIntIDGenerator = BigIntIDGenerator; window.BigIntHelper = BigIntHelper;}