超出JavaScript安全整数限制的数字计算BigInt详解


Posted in Javascript onJune 24, 2018

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位无符号的。

Javascript 相关文章推荐
JavaScript的类型转换(字符转数字 数字转字符)
Aug 30 Javascript
JS表单验证的代码(常用)
Apr 08 Javascript
全面解析Javascript无限添加QQ好友原理
Jun 15 Javascript
javascript时间戳和日期字符串相互转换代码(超简单)
Jun 22 Javascript
jquery轮播的实现方式 附完整实例
Jul 28 Javascript
node+koa实现数据mock接口的方法
Sep 20 Javascript
基于Node.js实现压缩和解压缩的方法
Feb 13 Javascript
JS实现读取xml内容并输出到div中的方法示例
Apr 19 Javascript
基于ts的动态接口数据配置的详解
Dec 18 Javascript
在vue中使用Echarts利用watch做动态数据渲染操作
Jul 20 Javascript
vue下拉刷新组件的开发及slot的使用详解
Dec 23 Vue.js
抖音短视频(douyin)去水印工具的实现代码
Mar 30 Javascript
JS的函数调用栈stack size的计算方法
Jun 24 #Javascript
JavaScript中var、let、const区别浅析
Jun 24 #Javascript
使用JavaScript中的lodash编写双色球效果
Jun 24 #Javascript
Vue中$refs的用法详解
Jun 24 #Javascript
JS实现获取word文档内容并输出显示到html页面示例
Jun 23 #Javascript
纯JS实现的读取excel文件内容功能示例【支持所有浏览器】
Jun 23 #Javascript
Vue子组件向父组件通信与父组件调用子组件中的方法
Jun 22 #Javascript
You might like
使用PHP制作新闻系统的思路
2006/10/09 PHP
PHP的简易冒泡法代码分享
2012/08/28 PHP
php的闭包(Closure)匿名函数详解
2015/02/22 PHP
laravel解决迁移文件一次删除创建字段报错的问题
2019/10/24 PHP
一个加载js文件的小脚本
2007/06/28 Javascript
jquery中对表单的基本操作代码
2010/07/29 Javascript
如何实现JavaScript动态加载CSS和JS文件
2020/12/28 Javascript
深入分析javascript中console命令
2016/08/14 Javascript
使用React实现轮播效果组件示例代码
2016/09/05 Javascript
JavaScrpt判断一个数是否是质数的实例代码
2017/06/11 Javascript
使用原生js封装的ajax实例(兼容jsonp)
2017/10/12 Javascript
vue项目中jsonp跨域获取qq音乐首页推荐问题
2018/05/30 Javascript
在vue中使用防抖和节流,防止重复点击或重复上拉加载实例
2019/11/13 Javascript
javascript设计模式 ? 外观模式原理与用法实例分析
2020/04/15 Javascript
vue Treeselect 树形下拉框:获取选中节点的ids和lables操作
2020/08/15 Javascript
Antd中单个DatePicker限定时间输入范围操作
2020/10/29 Javascript
[01:02:53]DOTA2上海特级锦标赛主赛事日 - 5 总决赛Liquid VS Secret第二局
2016/03/06 DOTA
Python httplib模块使用实例
2015/04/11 Python
Python学习小技巧之列表项的推导式与过滤操作
2017/05/20 Python
Python编写一个优美的下载器
2018/04/15 Python
在Python中获取操作系统的进程信息
2019/08/27 Python
新年福利来一波之Python轻松集齐五福(demo)
2020/01/20 Python
解决python使用list()时总是报错的问题
2020/05/05 Python
Python监听键盘和鼠标事件的示例代码
2020/11/18 Python
HTML5+CSS3 实现灵动的动画 TAB 切换效果(DEMO)
2017/09/15 HTML / CSS
CSS3自定义滚动条样式 ::webkit-scrollbar的示例代码详解
2020/06/01 HTML / CSS
免费获得微软MCSD证书赶快行动吧!
2012/11/13 HTML / CSS
电子信息毕业生自荐信
2013/11/16 职场文书
大一学生的职业生涯规划书范文
2014/01/19 职场文书
初一家长会邀请函
2014/01/31 职场文书
大四学生找工作的自荐信
2014/03/27 职场文书
共产党员岗位承诺书
2014/05/29 职场文书
公司总经理任命书
2014/06/05 职场文书
就业协议书盖章的注意事项
2014/09/28 职场文书
2015年药店店长工作总结
2015/04/29 职场文书
一文读懂navicat for mysql基础知识
2021/05/31 MySQL