Big Number in JavaScript

JavaScript

JavaScript 中可以很方便在字符串和数字之间进行转换,比如:+'123' => 123(123).toString() => '123'

然而,需要注意的一点是,JavaScript 中的数字并不是整数,而是浮点数。更确切的说,数字使用的是 64 bit 双精度浮点数来表示的。这意味着,如果服务器存储的数字是一个 Int64,那么在给到前端的时候,很有可能会出现转化上的问题。对于双精度浮点数来说,能够表示的最大的数是 25312^{53}-1,超过的部分就会被截断,无法精确表示。

比如:

console.log(+'9223372036854775808');
console.log(2 ** 63)
// output: 9223372036854776000

JavaScript 提供了 Number.isSafeInteger 这个 API 来判断一个数字是否是在可表示的安全范围内。比如:

console.log(Number.isSafeInteger(2 ** 63));
// output: false
console.log(Number.isSafeInteger(9223372036854776000));
// output: false
console.log(Number.isSafeInteger(2 ** 53 - 1));
// output: true

这里,2 ** 53 - 1 就是 JavaScript 中可以表示的最大整数,Number.MAX_SAFE_INTEGER 这个常量也等于这个值。超过这个数值的所有值都会被认为是不安全的,哪怕该值实际表示的结果“凑巧”是正确的。上例中,9223372036854776000 这个数字的表示结果“刚好”就是 9223372036854776000 本身,但是因为这个数已经超过了 25312^{53} - 1,所以依然被判定为是不安全的。

虽然 JavaScript 本身的数字不支持大数,但是 Chrome 已经集成了 BigInt 数据类型,它可以被用于表示任意大的整形数字,可以用于这样的使用场景。(注:BigInt 本身还在 staging 3,并不是标准的一部分)

简单的使用方法如下:

const num = BigInt(2 ** 63);
// or:
// const num = BigInt('9223372036854776000');
console.log(num);
// output: 9223372036854775808n
console.log(typeof num);
// output: bigint

需要注意的是,BigInt 不可以使用 new 运算符,否则会报错。直接像函数一样传递参数调用就可以了。

BigInt 也是支持数字运算的,运算的结果依然是 BigInt

console.log(1n + 2n); // => 3n
console.log(3n - 1n); // => 2n
console.log(2n * 3n); // => 6n
console.log(5n / 2n); // => 2n

特别需要注意的是,因为是整型数字之间的转换,所以在做除法的时候,不会出现小数。在上面的例子中,5n2n 的除法,结果是 2n 而不是 2.5,这一个行为和 C 中两个 Integer 之间除法的行为是一致的。

另外,BigInt 不支持和其他的数据类型进行混合计算。比如:1n + 2 这样的计算是会报错的,需要显式的进行类型转换后,才可以进行运算。这一点,和 JavaScript 中其他数据类型之间随意混乱的运算行为是不同的(比如,1 + '2' 这样的计算 JavaScript 就不会报错,还会得到 '12' 这样怪异的结果)。

虽然 BigInt 不允许和一般的 Number 进行混合计算,但是比较运算符是可以在两者之间进行比较的。比如:1n < 22n > 1 这些都是成立的。BigIntNumber 之间无法取得 === 的严格等价关系,但是 == 的比较是可能成立的。换句话说:1n == 1 是成立的,但是 1n === 1 是不成立的。

更多关于 BigInt 的行为,可以参考 MDN