JavaScript 中的基本数据类 Number 是双精度浮点数, 它可以表示的最大安全范围是正负 9007199254740991, 也就是 2 的 53 次方减一, 在浏览器控制台分别输入 Number.MAX_SAFE_INTEGER 和 Number.MIN_SAFE_INTEGER 可查看对应的最大 / 小值
- const max = Number.MAX_SAFE_INTEGER;
- // 9_007_199_254_740_991
- // 注意: 为了便于阅读, 我使用下划线作为分隔符将这些数字分组为千位数. 数字文字分隔符提案对普通的 JavaScript 数字文字使用正确.
将这个最大值加一, 可以得到预期的结果:
- max + 1;
- // 9_007_199_254_740_992
但是, 如果我们再次增加它, 结果不再可以完全表示为 JavaScript Number:
- max + 2;
- // 9_007_199_254_740_992
我们会发现 max+1 和 max+2 的结果一样. 只要我们在 JavaScript 中获得这个特定的值, 就无法判断它是否准确. 对安全整数范围以外的整数 (即从
Number.MIN_SAFE_INTEGER
到
Number.MAX_SAFE_INTEGER
) 的任何计算可能会失去精度. 出于这个原因, 我们只能依靠安全范围内的数字整数值.
BigInt
BigInt 是 JavaScript 中的一个新的原始类型, 可以用任意精度表示整数. 使用 BigInt, 即使超出 JavaScript Number 的安全整数限制, 也可以安全地存储和操作大整数.
chrome 67 + 开始支持 BigInt, 本文所有 demo 都是基于 chrome 67.
要创建一个 BigInt, 在数字后面添加 n 后缀即可, 例如,
123
变成 123n. 全局 BigInt(number) 函数可以用来将 Number 转换成 BigInt. 换句话说,
BigInt(123) === 123n
. 让我们用这两种技术来解决我们之前遇到的问题:
- BigInt(Number.MAX_SAFE_INTEGER) + 2n;
- // 9_007_199_254_740_993n
我们将两个 Number 相乘:
- 1234567890123456789 * 123;
- // 151851850485185200000
查看上面两个数字, 末尾分别是 9 和 3,9*3=27, 然而结果末尾却是 000, 明显是错误的, 让我们用 BigInt 代替:
- 1234567890123456789n * 123n;
- // 151851850485185185047n
这次我们得到了正确的结果.
Number 的安全整数限制不适用于 BigInt. 因此, BigInt 我们可以执行正确的整数运算而不必担心失去精度.
BigInt 是 JavaScript 语言中的一个原始类型. 因此, 可以使用 typeof 操作符检测到这种类型:
- typeof 123;
- // 'number'
- typeof 123n;
- // 'bigint'
因为 BigInts 是一个单独的类型, 所以 a BigInt 永远不会等于 a Number, 例如 42n !== 42. 要比较 a BigInt 和 a Number, 在比较之前将其中一个转换为另一个的类型或使用 abstract equal(==):
- 42n === BigInt(42);
- // true
- 42n == 42;
- // true
当强制转换为布尔型 (使用 if,&&,||, 或 Boolean(int)),BigInt 按照和 Number 相同的逻辑转换.
- if (0n) {
- console.log('if');
- } else {
- console.log('else');
- }
- // logs 'else', because `0n` is falsy.
运算符
BigInt 支持最常见的运算符, 二元运算符 +,-,*,**,/,% 都正常工作, 按位操作 |,&, <<,
- >> 和 Number 是一样的
- (7 + 6 - 5) * 4 ** 3 / 2 % 3;
- // 1
- (7n + 6n - 5n) * 4n ** 3n / 2n % 3n;
- // 1n
一元运算符 - 可以用来表示一个负值 BigInt, 例如 - 42n. 一元 + 是不支持的, 因为它会破坏 asm.js 代码, 在 asm.js 中 + x 总是抛出异常.
另外一个问题是, 不允许在 BigInt 和 Number
之间混合运算. 看看这个例子:
- BigInt(Number.MAX_SAFE_INTEGER) + 2.5;
- // ??
结果应该是什么? 这里没有好的答案. BigInt 不能表示小数, 并且 Number 不能表示 BigInt 超出安全整数限制的数字. 因此, BigInt 和 Number
之间的混合操作会导致
TypeError 异常.
这个规则的唯一例外是比较运算符, 比如 ===(如前所述) <, 并且 >=- 因为它们返回布尔值, 所以不存在精度损失的风险.
- 1 + 1n;
- // TypeError
- 123 < 124n;
- // true
- API
全局 BigInt 构造函数与构造函数 Number 类似: 将其参数转换为 BigInt(如前所述). 如果转换失败, 它抛出一个 SyntaxError 或 RangeError 异常.
- BigInt(123);
- // 123n
- BigInt(1.5);
- // RangeError
- BigInt('1.5');
- // SyntaxError
两个库函数启用将 BigInt 值封装为有符号或无符号整数, 限于特定的位数.
BigInt.asIntN(width, value)
将一个 BigInt 值包装为一个 width-digit 二进制有符号整数, 并将
BigInt.asUintN(width, value)
一个 BigInt 值包装为一个 width-digit 二进制无符号整数. 例如, 如果您正在执行 64 位算术, 则可以使用这些 API 来保持适当的范围:
- // Highest possible BigInt value that can be represented as a
- // signed 64-bit integer.
- const max = 2n ** (64n - 1n) - 1n;
- BigInt.asIntN(64, max);
- 9223372036854775807n
- BigInt.asIntN(64, max + 1n);
- // -9223372036854775808n
- // ^ negative because of overflow
请注意, 只要我们传递 BigInt 超过 64 位整数范围的值 (例如, 绝对数值为 63 位 + 符号为 1 位), 就会发生溢出.
BigInt 可以准确地表示 64 位有符号和无符号整数, 这些常用于其他编程语言. 两种新类型的数组风格, BigInt64Array 并且 BigUint64Array 更容易有效地表示和操作这些值的列表:
- const view = new BigInt64Array(4);
- // [0n, 0n, 0n, 0n]
- view.length;
- // 4
- view[0];
- // 0n
- view[0] = 42n;
- view[0];
- // 42n
BigInt64Array 确保其值是 64 位有符号的.
- // Highest possible BigInt value that can be represented as a
- // signed 64-bit integer.
- const max = 2n ** (64n - 1n) - 1n;
- view[0] = max;
- view[0];
- // 9_223_372_036_854_775_807n
- view[0] = max + 1n;
- view[0];
- // -9_223_372_036_854_775_808n
- // ^ negative because of overflow
- BigUint64Array 确保这些值是
64 位无符号的.
来源: https://www.cnblogs.com/wangmeijian/p/9217352.html