Blog
Blog copied to clipboard
变量和类型 - 数字精度丢失(0.1 + 0.2 = 0.30000000000000004)
数字精度丢失(0.1 + 0.2 = 0.30000000000000004)
IEEE754
JavaScript中的Number
类型是基于 IEEE 754 标准的双精度 64 位二进制格式的值。
- 符号位:1位,标识数值正负,0为正,1为负;
-
指数部分:11位,表示范围为
0~2047
,减去偏移常数bias
为1023,即实际范围为-1023~1024
;-
展开指数部分详细讲解
指数偏移常量:计算指数时要减去的常量。 指数位数若为e,指数偏移常量
bias
则为 2e-1,双精度64位浮点数中,指数位数为11位,故偏移常量为1023
,指数最终取值为0 - 1023
~2047 - 1023
,即-1023~1024
。特殊指数:指数全0或全1有特殊含义,不算正常指数。
- 指数全0,尾数全0,表示
0
。根据符号位不同可以分为+0
和-0
。 - 指数全0,尾数不为全0,这些数是非规范数,即尾数部分假设前面为0,而不是1。此时指数取最后一位为1时的值,64位双精度浮点数格式中为
-1022
。 - 指数全1,尾数全0,表示无穷大,即
Infinity
。根据符号位不同可以分为+Infinity
和-Infinity
。 - 指数全1,尾数不为全0,表示NaN,即
Not a Number
,不是数。
- 指数全0,尾数全0,表示
-
-
尾数部分:52位,二进制只有0和1,一个数值最终都可用
1.xxx
* 2 e 表示,故尾数部分表示小数点后的部分,小数点前默认有1。
0.1 + 0.2 = 0.30000000000000004
JS使用双精度 64 位二进制格式存储数值,所以先要将0.1和0.2转换为二进制。
0.1和0.2转换为二进制后是无限循环的,但是存储位是有限的,所以超出的部分要作“零舍一入”,这是第一步精度丢失。
相加时,由于两数指数级不相等,要进行“对位”操作,而且相加后产生了进位,这两个原因导致存储位不够用,超出部分又要“零舍一入”,这是第二次精度丢失,导致计算结果出现偏差。
具体计算分析过程参考 IEEE754 浮点数格式 与 Javascript number 的特性。
Number
上各个静态常量的理解
指数不取全0或全1的原因详见文章上方指数部分详细讲解
Number.MAX_VALUE
和 Number.MIN_VALUE
Number.MAX_VALUE
:可表示的最大正值: 当符号位为0、指数位除最后一位全为1、尾数位全为1时,为可表示的最大正值。
Number.MIN_VALUE
:可表示的最小正值: 当符号位为0、指数位全为0、尾数最后一位为1时,为可表示的最小正值。
// Number.MAX_VALUE
expect((2 - Math.pow(2, -52)) * Math.pow(2, 1023)).toBe(Number.MAX_VALUE)
// Number.MIN_VALUE
expect(Math.pow(2, -52) * Math.pow(2, -1022)).toBe(Number.MIN_VALUE)
Number.MAX_SAFE_INTEGER
和 Number.MIN_SAFE_INTEGER
Number.MAX_SAFE_INTEGER
:可表示的最大准确整数: 当符号位为0、指数实际值为52、尾数位全为1,即尾数每一位都表示整数时,为可表示的最大准确整数。
Number.MIN_SAFE_INTEGER
:可表示的最小准确整数: 当符号位为1、指数实际值为52、尾数位全为1,即尾数每一位都表示整数时,为可表示的最小准确整数。
// Number.MAX_SAFE_INTEGER
expect((2 - Math.pow(2, -52)) * Math.pow(2, 52)).toBe(Number.MAX_SAFE_INTEGER)
// Number.MIN_SAFE_INTEGER
expect((-1) * (2 - Math.pow(2, -52)) * Math.pow(2, 52)).toBe(Number.MIN_SAFE_INTEGER)
Number.EPSILON
大于1的最小可表示数与1的差:符号位为0,指数实际值为0,尾数最后一位为1时的数减去1,为Number.EPSILON
。即 2-52
// Number.EPSILON
expect((1 + Math.pow(2, -52)) - 1).toBe(Number.EPSILON)