JS中数值类型的本质

一、JS中的数值类型众所JS爱好友周知,JS中只有一个总的数值类型——number,它包含了整型、浮点型等数值类型 。其中,浮点数的实现思想有点复杂,它把一个数拆成两部分来存储 。第一部分是有效位数,也可以称作系数、分数或者尾数;第二部分被称为指数,表示小数点位应该插在系数的哪个位置 。在JS中,浮点数由IEEE 754标准非完全实现 。一个number包含1个符号位,11位指数为和53位有效位数 。IEEE 754基于二进制运作,分为两个部分组成 。第一部分包含两个子部分:符号位和有效位数 。符号位在最高位中,该位为1表示该数是负数,为0则表示正数;有效位数在64位的最低几位中,通常表示一个范围内的小数 。
0.5 <= 有效位数 < 1.0
按照这种表示法,有效位数的最高位理论上始终为1 。因此,该位实际上并不需要被存放于number 当中,于是就多出了能用的1位,称作彩蛋位 。
第二部分指数存在于符号位和有效位数之间那些位中 。存放号后,可以用如下公式表示一个数值:
数值 = 符号位 * 系数 * (2 ** 指数)
下面来分析一下数值类型的本质 。
二、JS中数值类型的本质在了解JS中数值类型的本质之前,我们先来看一个问题:0.1 + 0.2 = ?
我相信很多人都见过JS中这个经典的计算 。是的,0.1 + 0.2 != 0.3,而是等于0.30000000000000004 。这是为什么呢,下面我们就来剖析一下这种现象出现的本质原因 。看一下代码:
function deconstruct(number) {let sign = 1;let coefficient = number;let exponent = 0;//将符号位从系数中提取出来if (coefficient < 0) {coefficient = -coefficient;sign = -1;}if (Number.isFinite(number) && number !== 0) {//-1128 就是 Number.MIN_VALUE的指数减去有效位数再减去彩蛋位的结果exponent = -1128;let reduction = coefficient;//将系数不断除以 2,直到趋近于 0 为止while (reduction !== 0) {//将除的次数与-1128相加到exponentexponent += 1;reduction /= 2;}//当指数为 0 的时候,可以认为数值是一个整数//如果指数不为0,则通过校正系数来使其为 0reduction = exponent;while (reduction > 0) {coefficient /= 2;reduction -= 1;}while (reduction < 0) {coefficient *= 2;reduction += 1;}}return {sign,coefficient,exponent,number};}现在,我们看看传入1的结果:

JS中数值类型的本质

文章插图
【JS中数值类型的本质】根据数值公式计算:1 * 9007199254740992 * (2 ** -53) = 1
这显然是没有问题的 。
那么我们将0.1传入函数呢,会产生怎样的结果,接着来看 。
JS中数值类型的本质

文章插图
根据数值公式计算:
1 * 7205759403792794 * 2 ** -56 = 0.1000000000000000055511151231257827021181583404541015625
结果出人意料!JS无法精确的处理小数 。这是由IEEE 754机制决定的 。
因此,0.1 + 0.2 != 0.3很好解释了 。
那么,我们怎么在业务中,使得0.1 + 0.2 = 0.3呢?下面给出解决办法:
JS中数值类型的本质

文章插图
Number.toFixed()用于保留小数点具体位数进行四舍五入,参数默认为0,则不保留小数 。Number.toPrecision()用于保留并四舍五入到指定的数字位数,默认全部保留,参数为0,则保留1位有效数字 。注意,以上两个函数均返回string类型的值,所以在这里需要将其显示转换为number类型 。

    经验总结扩展阅读