科学计数法
假设有两个数字
220000000000 和 22000000000
一眼看过去好像很难比较出大小
但是使用科学计数法就非常容易进行比较
2.2
∗
1
0
11
2.2
∗
1
0
10
2.2*10^{11}\\ 2.2*10^{10}
2.2∗10112.2∗1010
IEEE 754浮点数表示
Javascript
是使用双精度浮点数表示的。
- sign:符号位 1表示数字为负数,0表示为正数 (1位)
- exponent:阶码 (11位)
- mantissa: 尾数 (52位)
- 总共64位
看不懂就对了,我刚开始也看不懂,估计很快也忘了
举个🌰
整数11,用科学计数法表示
- 这是一个正数,sign:符号位表示为 0
右上角1次幂就是阶码:exponent,但是还需要加上一个值1023,书上叫偏置值
。
主要是因为
能够正常使用的数有
0111111111
1
2
=
1023
1000000000
0
2
=
−
1024
011 1111 1111_2=1023\\ 100 0000 0000_2=-1024
011111111112=1023100000000002=−1024
为了方便进行浮点数之间的数学比较,需要保证阶码总是为无符号数
有符号数转成无符号数进行比较效率跟整型(如C语言的int类型)相同
所以这里要加上一个偏置值,保证exponent的值总是>0
11位数字的范围明明是1023~ -1024,又要保证其>0,为什么添加的偏置值只有1023 ?
主要是因为111 1111 1111
和000 0000 0000
是两种特殊情况,排除在外,所以只需要+1023就能保证表示范围>0
(图中的K就是偏置值Bias)
综上exponent的值为 100 0000 0000
3.mantissa: 尾数
(有些书上fraction也是一个意思)
假设我们只表示100
,1100
,11111
这样的数字,它们的科学计数法都是以1.xxx开头的,这样的话,1.xxx部分就可以省去,仅表示小数点.后面的数字即可
综合以上整数11可以表示为
200 300 这样的数字如何表示?
事实上,计算机的数字都是以二进制形式存储的,刚才的11
在二进制表示是3
。也就是说,刚才存储的实际上是十进制3
而不是十进制11
意思就是只需要将刚才的
改成2,就是在计算机中存储3
的浮点数值。
为什么0.1 + 0.2 不等于 0.3
Js中数字都采用双精度浮点数表示
首先要将0.1
转成二进制形式表示,小数转成二进制的方式就是不断*2,结果如果>1,说明该位需要用1表示,然后保留小数部分,继续乘直到得到0
0.1的二进制表示
熟悉的科学计数法表示
尾数(mantissa)是有限位数,能存放52位数字。
注意高亮的部分,52位后的部分就进行舍入,舍入类似于十进制的四舍五入,1则进位,0则不变。
这里发生的实际上是一个向上舍入,也就是得到的数字会比真实的数字大
再来计算它的阶码
0.1
的最终存储形式为
0.2
也是类似的,尾数部分第53位也是1,意味着也发生了向上舍入
计算0.1+0.2
科学计数法表示两个数
调整0.1的表示方法,跟0.2对齐
进行运算
计算结果
同样是超出了52位,而且第53位为1,说明真正存储的时候会发生向上舍入。
为了计算0.1+0.2
整整发生了3次向上舍入!
这也是为什么控制台打印得到的结果会比实际结果大。
循环永远不会结束
for(let i = 0; i = 9007199254740992; i++){
//never stop
}
这个循环将永远不会结束。
javascript
中有一个Number.MAX_SAFE_INTEGER
打印的结果是9007199254740991 (2⁵³−1)
可以看到,超出这个最大安全整型,就会出现一些奇怪的事情
9007199254740991浮点表示
注意,尾数部分全为1
将其转成科学计数法
2⁵³−1实际上就是尾数部分,是尾数部分能够表示的极限了,因此9007199254740991这个数值就是使用双精度浮点数能表示最大的数值。
特殊情况NaN Infinity
刚才计算exponent的时候偏置值并不是1025,主要是因为另外两个独立的存在
其中一个独立的存在是阶码全为1的情况
NaN
Infinity
无穷大
另一个阶码全为0的情况不多做讨论,书上称为非规格化数,它提供一种表示0的方法
符号位为1,其他位都为0,可以表示负-0.0
符号位为0,其他位都为0,可以表示+0.0
详情参考
《深入理解计算机系统》第二章