图解:JavaScript中Number的一些表示上/下限

24

自己整理、设计的,转载请注明原帖。先从这个demo看起:http://alvarto.github.io/Visu...

数轴

请输入图片描述

说明

关于Number表示的内存模型

参考国际标准IEEE 754,我画了一张图帮助理解:

请输入图片描述

注,这里的字符是从左到右排的,和wiki之类的资料顺序相反。wiki资料考虑的是比较的顺序(符号-指数位-有效数字),而我这里考虑到的是阅读顺序(从0到63位,从左到右)。

中间的指数位是如何同时表示正负指数值的呢,和“符号位+有效数字位”的常规表示方法不同,指数是使用偏移法来做的:

IEEE 754:指数偏移值

指数偏移值(exponent bias),是指浮点数表示法中的指数域的编码值为指数的实际值加上某个固定的值,IEEE 754标准规定该固定值为2^(e-1)-1,其中的e为存储指数的比特的长度。
以单精度浮点数为例,它的指数域是8个比特,固定偏移值是28-1 - 1 = 128−1 = 127.单精度浮点数的指数部分实际取值是从128到-127。例如指数实际值为1710,在单精度浮点数中的指数域编码值为14410,即14410 = 1710 + 12710.
采用指数的实际值加上固定的偏移值的办法表示浮点数的指数,好处是可以用长度为e个比特的无符号整数来表示所有的指数取值,这使得两个浮点数的指数大小的比较更为容易。

因此,在JavaScript里面的指数位,是从1-2^(11-1),也就是从-1023开始,表示了(-1023,1024)这个区间。

实际指数值 存储的指数值
-1022 1
0 1023
1023 2046

Number保留了指数值0和2047用于表示一些特殊的值。总的表示表格如下:

X Y 表示的值
=0 =0 ±0
≠0 =2047 NaN
=0 =2047 ±Infinity
≠0 =0 反规格化值(Denormalized):f(0.x , 1 , z)
∈(0,2047) 规格化值(Normalized):f(1.x , y , z)
<h3>f(i,j,k) = (-1)k · 2-1023+j · i</h3>

精确表示到个位的最大整数

前52位能表示的最大值是下面这个(下面是52位+1位默认的1):

parseInt("11111111111111111111111111111111111111111111111111111",2)
-> 9007199254740991 //即2^53-1

而下一个值是:

parseInt("100000000000000000000000000000000000000000000000000000",2)
-> 9007199254740992 //即2^53

根据内存模型,画一张图就可以知道:

请输入图片描述

从第2^53位开始,第一个进制被舍弃,这个时候,2^53+1==2^53,每两个值都会有一个值出现这种不精确的情形。再过N个值,会出现每4个值里面都有3个值不精确;再过M个值,会出现每2^K个值里有2^K-1个值不精确;以此类推……(小题目:这个N值是多少?)

最大可表示的正数

请输入图片描述

验证:

Number.MAX_VALUE.toString(2)
-> "1111111111111111111111111111111111111111111111111111100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"

var a = Number.MAX_VALUE.toString(2).split("") , b = [ a.filter(function(i){return i==0}).length , a.filter(function(i){return i==1}).length ] ; b
-> [971, 53]

Number.MAX_VALUE === (Math.pow(2,53)-1)*Math.pow(2,971)
-> true

QED

最小可表示的正数

还记得前面的表格吗:

X Y 表示的值
≠0 =0 反规格化值(Denormalized):f(0.x , 1 , z)
∈(0,2047) 规格化值(Normalized):f(1.x , y , z)
<h3>f(i,j,k) = (-1)k · 2-1023+j · i</h3>

非规格化值是这样表示的:

请输入图片描述

最小正数的内存模型

请输入图片描述

验证:

Number.MIN_VALUE.toString(2)
-> "0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001"

var a = Number.MIN_VALUE.toString(2).split(""); a.filter(function(i){return i==0}).length - 1
-> 1073

Number.MIN_VALUE === Math.pow(2,-1074)
-> true

参考资料

除了IEEE 754的维基页面,还有这篇文章,解释的非常清晰:"How numbers are encoded in JavaScript"

最后再推一次:输入表达式,返回对应Number值的内存模型的DEMO
http://alvarto.github.io/Visu...

你可能感兴趣的

15 条评论
P酱 · 2014年02月13日

清晰易懂,感谢 @Humphry 的研究整理。这回完全弄清楚了。

回复

天镶 · 2014年02月14日

好赞,学习了

回复

Humphry 作者 · 2014年02月14日

这位客官,学习了要点赞呀!

回复

天镶 · 2014年02月14日

抱歉抱歉,关顾着看文了,已点

回复

miser · 2014年02月19日

感觉图有些奇怪,貌似左右方向反了吧

回复

Humphry 作者 · 2014年02月19日

我是特地拧过来的……因为wiki上是右边那一位是0位,个人觉得不符合阅读习惯……

回复

miser · 2014年02月19日

怎么说呢,我看到的各类书籍都是把表示正负的符号位放在最左的,其次wiki上的

浮点数的比较
浮点数基本上可以按照符号位、指数域、尾数域的顺序作字典比较。显然,所有正数大于负数;正负号相同时,指数的二进制表示法更大的其浮点数值更大。

从左向右比较感觉比较符合阅读习惯
纯粹个人观点,感觉不好可以无视:)

回复

Humphry 作者 · 2014年02月19日

哦是为了按照字典顺序比较的问题呀……改图太麻烦了,我在文中加一个声明吧,谢谢指正!

回复

JerryZou · 2014年05月19日

很不错的分享,必须给赞~

回复

linkarys · 2014年05月28日

图很靓啊,用什么软件画的?

回复

Humphry 作者 · 2014年05月28日

Illustrator……

回复

linkarys · 2014年05月28日

高端大气!Thanks!

回复

新しい世界 · 2017年02月12日

不错,数轴的图很直观。建议如果是很长一串0或者1,写成 "0".repeat(n) 会更直观一点,,毕竟重要的是有多少位

回复

0

哈哈,当初写这个文章的时候还没有这个接口

Humphry 作者 · 2017年02月13日
0

@Humphry ?我回复之前没看时间,才发现挖坟了

新しい世界 · 2017年02月13日
载入中...