JavaScriptのNumber型で正確に表せる範囲は、最大で 9007199254740991
(2^53 − 1
)、最小で −9007199254740991
(−(2^53 − 1)
)
個人的に読みにくいので日本語で読みやすく書くと、9007兆1992億5474万0991
が最大です
最大値、最小値はそれぞれ、Number.MAX_SAFE_INTEGER
、Number.MIN_SAFE_INTEGER
で定義されています。
console.log(Number.MAX_SAFE_INTEGER) // 9007199254740991
console.log(Number.MIN_SAFE_INTEGER) // -9007199254740991
この範囲外の数字だとJavaScritptでは正しく扱えない場合があります
参考:Number.MAX_SAFE_INTEGER - JavaScript | MDN
const x = Number.MAX_SAFE_INTEGER + 1;
const y = Number.MAX_SAFE_INTEGER + 2;
console.log(x === y); // true
console.log(9007199254740992 === 9007199254740993) // true
console.log(9007199254740992 < 9007199254740993) // false
↑のようにNumber.MAX_SAFE_INTEGER
より大きい数字はうまく動いてくれません。なのでBigIntを使うことで正しく扱うことができます。
BigIntとは
仕様
2^53
より大きい値の整数を表せるデータ型
String
、Number
、null
、undefind
、Symbol
などと同じくBigInt
もデータ型の一つ
私はあまり詳しくないのですが、こちらのcommitによると、2019の9月に stage 4 となり仕様が確定したJavaScriptの新しい機能です。
https://github.com/tc39/proposal-bigint/commit/771df8ff3516ea5f3397153d17d04e6c6b75a4ce
構文
整数の末尾にn
を付けるか、BigInt(数字)
とする
// 末尾にnをつける
console.log(9007199254740991n);
// BigIntを呼び出す
console.log(BigInt(9007199254740991));
// 文字列でもOK
console.log(BigInt("9007199254740991"));
// 16進数でもOK
console.log(0x1fffffffffffffn);
console.log(BigInt(0x1fffffffffffff));
// 8進数でもOK
console.log(0o377777777777777777n);
console.log(BigInt(0o377777777777777777));
// 2進数でもOK
console.log(0b11111111111111111111111111111111111111111111111111111n);
console.log(BigInt(0b11111111111111111111111111111111111111111111111111111));
typeof
は'bigint'
を返す
console.log(typeof 1n); // 'bigint'
console.log(typeof BigInt(1)); // 'bigint'
console.log(typeof 1n === 'bigint'); // true
console.log(typeof BigInt(1) === 'bigint'); // true
Number.MAX_SAFE_INTEGER
以上の値で試してみる
// Numberで正確に表せる最大値
console.log(Number.MAX_SAFE_INTEGER) // 9007199254740991
console.log(9007199254740992 === 9007199254740993) // true
console.log(9007199254740992n === 9007199254740993n) // false
console.log(9007199254740992 < 9007199254740993) // false
console.log(9007199254740992n < 9007199254740993n) // true
// 最小より小さい値でも確認
console.log(Number.MIN_SAFE_INTEGER) // -9007199254740991
console.log(-9007199254740992 === -9007199254740993) // true
console.log(-9007199254740992n === -9007199254740993n) // false
console.log(-9007199254740992 > -9007199254740993) // false
console.log(-9007199254740992n > -9007199254740993n) // true
BigIntを使うことで桁が大きい値でも正しく扱うことができました。
四則演算
const previousMaxSafe = BigInt(Number.MAX_SAFE_INTEGER);
console.log(previousMaxSafe + 1n); // 9007199254740992n
console.log(previousMaxSafe * 2n); // 18014398509481982n
console.log(previousMaxSafe - 1n); // 9007199254740990n
console.log(4n / 2n); // 2n
// 結果が小数点となる場合は切り捨てられる
console.log(5n / 2n); // 2n
// 切り捨ては0に向かって丸め込まれるようです
console.log(-5n / 2n); // -2n
console.log(100n % 3n); // 1n
console.log(2n ** 3n); // 8n
console.log(+1n); // TypeError: Cannot convert a BigInt value to a number
console.log(-1n); // -1n
let max = Number.MAX_SAFE_INTEGER;
let min = Number.MIN_SAFE_INTEGER;
max++;
min--;
console.log(max) // 9007199254740992
console.log(min) // -9007199254740992
真偽値に変換
Numberと同じように扱える
console.log(Boolean(0n)) // false
console.log(Boolean(1n)) // true
if (1n) {
// 実行される
}
if (0n) {
// 実行されない
}
BigInt
を===
、==
で試してみた
console.log(10n === 10) // false
console.log(10n == 10) // true
console.log(10n === "10") // false
console.log(10n == "10") // true
console.log(1n === true) // false
console.log(1n == true) // true
Number
とBigInt
で四則演算
1 + 1n // TypeError: Cannot mix BigInt and other types, use explicit conversions
明示的に型変換しないとエラーになってしまう。
Number
とBigInt
で比較演算
Number
とBigInt
は通常どおりに比較できます
console.log(1n < 2) // true
console.log(2n > 1) // true
console.log(2n > 2) // false
console.log(2n >= 2) // true
sort
Number
とBigInt
が混在していてもソートできる
const mixed = [4n, 6, -12n, 10, 4, 0, 0n];
console.log(mixed.sort()); // [-12n, 0, 0n, 10, 4n, 4, 6]
※辞書順比較だと問題ないですが、コールバック関数で四則演算などした場合はエラーになりますので、型変換などしてソートを行うと良いと思います
@le_panda_noir さんからのコメント参考
mixed.sort((a, b) => a - b); // TypeError
Math
オブジェクトのメソッドでも使用できない
Math.round(1n) // TypeError: Cannot convert a BigInt value to a number
Math.max(1n, 10n) // TypeError: Cannot convert a BigInt value to a number
小数点
整数でないとBigIntは使えません
console.log(BigInt(1.5)) // RangeError: The number 1.5 cannot be converted to a BigInt because it is not an integer
console.log(BigInt('1.5')) // SyntaxError: Cannot convert 1.5 to a BigInt
Static methods
BigIntは2つのstatic関数を持っています
BigInt.asIntN(width, bigint)
第2引数で指定したbigintを -2 ** (width - 1)
〜 2 ** (width - 1) - 1
の範囲内で収まる、符号付きのBigIntの値を返します
// 64bitの範囲 -9223372036854775808 〜 9223372036854775807
const max = 2n ** (64n - 1n) - 1n; // 9223372036854775807n
console.log(BigInt.asIntN(64, max)); // 9223372036854775807n
// 溢れた場合
console.log(BigInt.asIntN(64, max + 1n)); // -9223372036854775808n
console.log(BigInt.asIntN(64, max + 2n)); // -9223372036854775807n
BigInt.asUintN(width, bigint)
第2引数で指定したbigintを 0
〜 2 ** width - 1
の範囲内で収まる、符号なしのBigIntの値を返します
// 64bitの範囲(符号なし) 0 〜 18446744073709551615
const max = 2n ** 64n - 1n; // 18446744073709551615n
console.log(BigInt.asUintN(64, max)); // 18446744073709551615n
// 溢れた場合
console.log(BigInt.asUintN(64, max + 1n)); // 0n
console.log(BigInt.asUintN(64, max + 2n)); // 1n
JSON.stringify
でうまく使えない
console.log(JSON.stringify({ a: 1n })) // TypeError: Do not know how to serialize a BigInt
toJSON
上書きすると、使えはするそうです
BigInt.prototype.toJSON = function() { return this.toString(); }
console.log(JSON.stringify({ a: 1n })) // '{"a":"1"}'
ブラウザサポート
使えないブラウザもいくつかあります
※2019/11/17確認
ブラウザ対応
JSBI
というライブラリを使うのが良いようです。
yarn add jsbi
node 12.12.0 で実行してみたサンプル
import JSBI from 'jsbi';
const max = JSBI.BigInt(Number.MAX_SAFE_INTEGER);
console.log(String(max));
// → '9007199254740991'
const other = JSBI.BigInt('2');
const result = JSBI.add(max, other);
console.log(String(result));
// → '9007199254740993'
$ node --experimental-modules index.mjs
(node:9580) ExperimentalWarning: The ESM module loader is experimental.
9007199254740991
9007199254740993
JSBI
を使っていれば、babel-plugin-transform-jsbi-to-bigint
を使うことで
JSBI
をBigInt
の構文にトランスパイルすることができます。
なのでIEやSafariなどのBigInt
がまだサポートされていないブラウザでも使いたい場合は、まずJSBI
でBigInt
の処理を実装します。
それらのブラウザでBigInt
がサポートされる日が来たら、babelでJSBI
をネイティブのBigInt
にトランスパイルします。
そうすることで書いたコードを変更することなく、ブラウザはネイティブのBigInt
を使用してくれます。
yarn add jsbi
yarn add -D @babel/core @babel/cli babel-plugin-transform-jsbi-to-bigint
このように、JSBI
のコードをネイティブのBigInt
に変換することができました
import JSBI from 'jsbi';
const max = JSBI.BigInt(Number.MAX_SAFE_INTEGER);
console.log(String(max));
// → '9007199254740991'
const other = JSBI.BigInt('2');
const result = JSBI.add(max, other);
console.log(String(result));
// → '9007199254740993'
$ yarn babel --plugins transform-jsbi-to-bigint index.js
/* 省略 */
const max = BigInt(Number.MAX_SAFE_INTEGER);
console.log(String(max)); // → '9007199254740991'
const other = 2n;
const result = max + other;
console.log(String(result)); // → '9007199254740993'
✨ Done in 0.40s.
参考
- tc39/proposal-bigint
- he Essential Guide To JavaScript’s Newest Data Type: BigInt — Smashing Magazine
- JavaScriptのBigIntで任意精度の整数値を扱う
- BigInt: arbitrary-precision integers in JavaScript · V8
- 2018-11-13のJS: Prettier 1.15のHTMLサポート、TypeScriptガイド、BigIntsのJavaScript実装 - JSer.info
- 【C言語/C++】データ型のサイズ・範囲の一覧【32bit/64bit環境】 | MaryCore
最後まで読んでいただいてありがとうございました。m(_ _)m