博客
电影
宝箱
友链
关于
<
《圣经》读书笔记
《三块广告牌》影评
>
深入理解IEEE754的64位双精度
作者:
Cifer
类别: 技术·JS
时间:2018-04-03 20:19:32
字数:3813
版权所有,未经允许,请勿转载,谢谢合作~
### 前言 最近在JavaScript使用整数时,遇到因为数字超出最大值的问题。回忆起Cifer中学时代玩魔兽世界,金币最大为21万4748金币36银币47铜币,即2147483647,当时并不懂原因,还以为暴雪想限制游戏平衡、防玩家沉迷。 简单理解,带符号的32位int中2^31-1 = 2147483647 。但在JS整数中,事情并不简单。 因为JS数字不区别整数与浮点数,只有Number类型,即都是浮点数,它采用IEEE 754标准的64位双精度格式(如果是索引数字则是32位单精度 )。 ### IEEE 754 的双精度 IEEE 754 标准为IEEE二进位浮点数算术标准,是由美国电气电子工程师学会(IEEE)发布。IEEE 754是最广泛使用的浮点数运算标准,为许多CPU与浮点运算器所采用,大部分编程语言都有提供IEEE格式与算术,但有些将其列为非必要的。 <a href="http://www.boatsky.com/blog/26">IEEE 754 的双精度</a>,由三部分组成: 1位数符 + 11位阶码 + 52位尾数 数符S(sign):标记正负,0为正,1为负 阶码E(exponent bias):阶码 = 阶码真值 + 偏移量 1023。偏移量 = 2^(k-1)-1,k表示阶码位数 尾数M(Mantissa):数字的小数部分 V = (-1)^Sx2^(E-1023)*1.M 其中1023是偏移量 32位单精度: 1位数符 + 8位阶码 + 23位尾数 与64位原理一样,本例只细讲64位。 #### 阶码E、阶码真值的区别 阶码真值即为科学记数法指数的真实值的2进制,它指明了小数点在尾数中的位置,阶码真值与偏移量相加得到阶码。简单理解,阶码真值是实际指数中二进制值,而阶码是指数偏移之后保存起来的二进制数据。 #### 为什么会有偏移量1023 11位代表的阶码,如果是无符号是[0,2047],去除0与2047两个非规格化情况(后面会讲到为什么是非规格化情况),变成[1,2046], 偏移之前,指数是[-1022,1023]。如果没有偏移量的存在,指数需引入符号位, 则需引入补码,比较计算更加复杂,为了简化操作,才使用无符号的阶码,引入偏移量的概念。 #### 尾数M实际有多少位 同一个浮点数的表示方式有很多,但规范中一般使用科学计数法,例如用5.123x10^3表示,而不用51.23x10^2或0.5123x10^4。二进制中只有0与1,那么按科学计数法,首位只可能是1,对此IEEE 754省略了这个默认的1,所以有效尾数实际上是有53位的。 这时出现一个问题,尾数M省略的1是一定会存在,以致于浮点数无法表示0.0 不过IEEE 754早就考虑到了这个问题。 #### E阶码分成三种情况 规格化: S + (E!=0 && E!=2047) + 1.M 非规格化: S + 000 00000000 + M 无穷大: S + 111 11111111 + 00000000 00000000 00000000 00000000 00000000 00000000 0000 无穷大的变种即NaN: S + 111 11111111 + (M!=0) 规格化的情况:即上述的一般情况,因为阶码不能为0也不能为2047,所以指数不能为-1023,也不会为1024,只有这种情况才会有隐含位1。 非规范化情况:此时阶码全为0,指数为-1023,如尾数全为0,则浮点数表示正负0;否则表示那些非常的接近于0.0的数。 #### 举个规格化的例子 十进制 -125.125,在JS内存中的二进制数据是多少 第1步,负号S为1,绝对值转成二进制:1111101.001,(整数小数分开计算,整数除2取余;小数乘2取整) 第2步,科学计数法:1.11110100 1 * 2^6 第3步,计算阶码: 000 00000110 (阶码真值) + 011 11111111 (偏移量) = 1 00 00000101 第4步,得到最终存储值:1 + 100 00000101 + 11110100 10000000 00000000 00000000 00000000 00000000 0000 #### 数字的范围 数的范围有两个概念,一为最大正数与最小负数,二为最小正数与最大负数,即[最小负数,最 大负数] 并上 [最小正数,最大正数] 从S、E、M三个维度看,S代表正负,E阶码值远大于M尾数个数,所以S阶码决定大小,M尾数决定精度。 从E阶码分析: 规格化下,当E最大值时, 2046-1023=1023,或 ,111 11111110 - 011 11111111 (偏移量) = 011 11111111 = 1023 从E的最大值,从指数来看,得知数值范围是 [-2^1023,2^1023],JS函数Math.pow(2,1023)结果是8.98846567431158e+307,然而如果尾数是,1.11111111 11111111 11111111 11111111 11111111 11111111 1111 ,则其接近于2,8.98846567431158 x 2 再合上原来的指数,约等于 1.797693134862316e+308 1.7976931348623157e+308,这个值是我们用JS常量Number.MAX_VALUE获取到的,两者非常相近。 所以数字的范围是[-1.7976931348623157e+308,1.7976931348623157e+308],如果超过这个值,则数字太大,在JS中会显示Infinity或-Infinity,叫正向溢出。 非规格化下,指数为,000 00000000 - 011 11111111 (偏移量) = - 100 00000001(转成10进制,减1取反)= - 1023 从指数看,得知最小值是2^-1023,然而如果尾数是0.00000000 00000000 00000000 00000000 00000000 00000000 0001 不为0的情况,52位尾数相当于小数点还能虚拟化的向右移动51,可以取得更小的 2^-51, 所以最小值为2^-1074 = Math.pow(2,-1074) 约等于 5e-324 而JS最小值常量Number.MIN_VALUE正是5e-324 所以(-5e-324,5e-324)之间的数比可表示的最小数还要小,显示成0,叫反向溢出。 #### 整数范围 从M尾数分析,精度最多是53位,精确整数的范围。 如M最大值,1.11111111 11111111 11111111 11111111 11111111 11111111 1111 即2^53-1 , 9007199254740991 可用Math.pow(2,53)-1 计算范围 [-9007199254740991, 9007199254740991], 恰好发现: JS最小安全整数 Number.MIN_SAFE_INTEGER: -9007199254740991 JS最大安全整数 Number.MAX_SAFE_INTEGER: 9007199254740991 如果整数是这个或者小数尾数在这个『范围』内,则是安全整数,可用函数Number.isSafeInteger()验证。 尾数M在52位范围内,1. 00000000 00000000 00000000 00000000 00000000 00000000 0001 可以认为是最小精度 2^-52,可用常量Number.EPSILON 1 2.220446049250313e-16 表示,可用它做精度判断、误差设置等。 ### JS中数的总结 | | JS的32位整数| 64位精度| |----------|:-------------:|------:| | 场景 | 数组可识别length索引、位运算 | 正常number | |整数范围 | 数组可识别length索引: [0, 4294967295] ,参与位运算的数字范围: [-2147483647, 2147483647] | [-9007199254740991, 9007199254740991] | | 可表示数的范围 | 同上 | [-1.7976931348623157e+308,-5e-324],[5e-324, 1.7976931348623157e+308] | |最小精度| 1 | 2.220446049250313e-16 | 一目了然,如果超过相关值时,需使用字符串手工运算转换了。又或者使用开源的 <a href="http://mikemcl.github.io/bignumber.js/" title="bignumber.js" target="_blank" rel="nofollow">bignumber.js</a> 了解64位双精度的实现过程之后,Cifer下面这篇文章就很好理解了:<a href="https://www.boatsky.com/blog/32">解决toFixed四舍五入陷阱</a>
如果觉得有帮忙,您可以在本页底部留言。
相关推荐:
NPM发包流程与技巧
Puppeteer爬取豆瓣电影TOP250评分
基于vue实现腾讯云点播的上传与播放
JS代理Proxy与反射Reflect场景
轻应用PWA实践全过程
ES6迭代器Iterator原理与性能
JavaScript之Set与Map
ES6设计模式之观察者模式
解决toFixed四舍五入陷阱
ES6设计模式之单例模式
ES6设计模式之工厂模式
ES6二叉树的实现
JavaScript链表实例
JavaScript排序算法及性能比较
原生ajax及jQuery封装ajax实例
JavaScript类的语法糖
……
更多
<
《圣经》读书笔记
《三块广告牌》影评
>
全部留言
我要留言
内容:
网名:
邮箱:
个人网站:
发表
全部留言