JavaScript的Number存储格式.

IEEE二进制浮点数算术标准(IEEE 754), 它规定了四种表示浮点数值的方式:单精度(32位),双精度(64位),延伸单精确度与延伸双精确度。在 JavaScript 中采用的是 64 位双精度存储的。这样存储结构优点可以归一化处理整数和小数。

64 位比特可以分为三部分来存储我们的数字。

 符号位S:第1位是正负符号位,0表示是正值,1代表是负值。 63位为指数位。

 指数位E:中间的11位存储指数,用来表示次方数。52-62位。 

 尾数位M:最后的52位尾数,超出的部分自动进一舍零。0-51位。

把多个比特的数据,从内存地址低端到高端,通常把最低端的放到最右边,称为最低有效位,代表最小的,改变时对整体的大小改动不大。

实际上用公式来计算的话:

 以上的公式遵循科学记数法的规范,在十进制中 0<M<10,到二进制就是 0<M<2。所以的话,整数部分就只能是1,按照IEEE 754 的描述,在尾数位之前,默认会有一个隐藏位1,有效数字将会被表示位 1.M, 所以1可以被舍去,只保留后面的小数部分。

前文说小数位称为有效位,最高有效位是1(它是隐藏的),小数位的最高有是1(即整数字),这个浮点数被称为规约形式的浮点数。代表它是唯一确定的使用浮点形式来表示一个值。

我们在来讲讲指数位的偏移。

指数部分有 11 位,E是一个无符号整数,取值范围是 0~2047, 但是这样子很难表示一个值是大的还是小的,在我们的价值观上是认为有符号,是负的,才是小的,否则就算大的。基于这一点,指数在存储之前需要做偏差修正,将它的值调整到一个 无符号数 的范围内以便进行比较。

为了表示负数,会取中间值来进行偏移   0 <= E <=2**11 - 1 ---->   0 <= E <=2047, 我们取中间值的话, 取到bias = 1023(表示一个中间值)。然后偏移结果就是。-1023 <= E-bias <= 1024.因此会得到用 1023 表示 0,用2046 表示 1023.

有两个特殊的 E 值。  0 和 2047

双精度(64-比特) 在指数偏移值的值域为 00000000001 (11-bit) 到 11111111110, 在小数部分则是00000...00000(52-bit) 到 11111...11111(52-bit)。

parseInt('1111111110', 2);  // 解析为 2046
parseInt('000000000001', 2);   // 解析为 1

parseInt('0000000000000000000000000000000000000000000000000000', 2) // 0;

当 E = 2047 时,并且 M 值为 0 时,该值表示为无穷大, 但如果 f > 0,该值则为 NaN。

当 E = 0 时,f 也为 0, 该值可能为 +0 也可能为 -0,因为符号是单独的一位,63位存储的。

先举个例子,0.1 是怎么存放的。

0.1 转为二进制。小数转二进制的方法应该是乘2取整,直到小数位为0。然后从上到下收集,然后别忘了还有前面的0。

0.2  0
0.4  0
0.8  0
1.6  1
1.2  1
0.4  0
0.8  0
1.6  1
1.2  1
0.4  0
0.8  0

所以最后0.1 的二进制为。0.00011001100(1100是无限循环的)。科学记数法表示 1.1001*2^-4,这样子 -4时存储到指数位,1.1001的1被隐藏,只剩下1001(无限1001),我们拿出52位存储到小数位。100110011001100110011001100110011001100110011(53位,小数点后是1,我们遵循进1舍0的方法)变为 10011001100110011001100110011001100110011010, E= -4+1023 = 1019(存储位2进制位01111111011),是正数,所以符号位存储的是0。

我们将上面的存储的二进制转回十进制,得到了 0.100000000000000005551115123126, 我们可以看出,这与 0.1 已经不相等了,出现了精度误差。

那么为什么还会得到我们的 0.1, 而不是一个丢失精度的数字。

我们可以使用 测试IEEE 754工具 来计算一下,得到的十进制数还是 0.1. 这是为什么呢,你可能会想,这怎么可能。

这是因为 IEEE 754 双精度 用二进制转为十进制,结果会很长,但是我们只会取到17位精度,即 0.10000000000000000(1后面16个0),因为存储是浮点数,所以可以去掉0,即为 0.1。

为什么0.1 + 0.2 === 0.30000000000000004;

可以把 0.1 和 0.2 分别转为对应的二进制。

0.00011001100110011001100110011001100110011001100110011010 +
0.0011001100110011001100110011001100110011001100110011010 =
0.0100110011001100110011001100110011001100110011001100111

// 转成十进制正好是 0.30000000000000004

所以说,存储一个数字,实际上是先转为计算机能识别的0,1机器码,二进制只能精准表达2除尽的数字1/2,1/4,1/8,例如0.1(1/10)和0.2(1/5),在二进制中都无法精准表示时,所以在存储的时候,就会发生进 1 舍 0,从而造成精度丢失。然后在读取为10进制的时候,就会与预期的不符。

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值