当前位置: 首页 > Web前端 > JavaScript

深入理解Number类型数据

时间:2023-03-27 01:01:04 JavaScript

在C语言中,由于数字的取值范围不同,需要用不同的数据类型来表示:int、long、double等。在弱类型语言JavaScript中,除了Number类型,没有其他数据类型表示数字。虽然在大多数场景下,Number类型代表的是数字,但是没有问题。但是当对数字取值的范围和精度提出更高要求时(金融场景),就会出现意想不到的情况,给代码的运行带来不稳定性。简介根据ECMA-262规范的定义,Number类型使用IEEE75464位双精度格式来表示内存中的数字:负数、0、整数、小数、Infinity,甚至NaN。IEEE75464位双精度格式这种格式由符号位(sign)、指数(exponent)和分数(fraction)组成:组成描述符号的位数(0表示正数,1表示负数)1exponentexponent11fractionfraction52expressionvalue类型除法IEEE75464位双精度格式,有11位指数,其存储值范围为:00000000000~11111111111。根据指数的存储值,IEEE75464-位双精度格式表达式值分为三种类型。当指数全为1(11111111111)时,表示特殊值。当52位小数部分全为0时:符号位为0,表示+Infinity;符号位为1,表示-Infinity。当52位小数部分不全为0时,表示NaN(NotaNumber)。当指数由0和1组成时,计算简化形式浮点数表达式值的公式为:\(\rm(-1)^s\times(1.\underbrace{bbb...b}_{f(52bits)})_2\times2^{e-1023}\)s:用符号(0或1)表示的值,可以确定表示值的符号。f:分数小数位,二进制小数位共52位。e:指数的存储值范围为1~2046(00000000001~11111111110);因为小数的绝对值需要表示的越小越好,所以exponent需要表示为负数;因此需要将每个指数的值固定减去偏移常数(1023),得到一个正负等分的指数表达式取值范围-1022~1023。偏移常数计算公式:\(\rm2^{n-1}-1\)。n为位数,这里索引的总位数为11。表达式取值范围符号为0,可以使符号为正号;分数的小数位全部为1,可以使实数最大;指数最大,可以使幂最大;这三部分带入计算得到最大的正数:$$\rm(-1)^0\times(1.\underbrace{111...1}_{52bits})_2\times2^{2046-1023}=(1.\underbrace{111...1}_{52-bit})_2\times2^{1023}=1.7976931348623157e+308$$与最大正数相比,符号改为一个负号,得到最小的负数:$$\rm(-1)^1\times(1.\underbrace{111...1}_{52bits})_2\times2^{2046-1023}=-(1.\underbrace{111...1}_{52bits})_2\times2^{1023}=-1.7976931348623157e+308$$sign为1,可以使sign为正号;分数的小数位全部为0,可以使实数最小;指数最小,可以使幂最小;将这三部分带入计算得到最小的正号数:$$\rm(-1)^0\times(1.\underbrace{000...0}_{52bits})_2\times2^{1-1023}=(1)_2\times2^{-1022}=2^{-1022}=2.2250738585072014e-308$$与最小的正数相比,符号改为负号,最大的负数获得数字:$$\rm(-1)^1\times(1.\underbrace{000...0}_{52bits})_2\times2^{1-1023}=-(1)_2\times2^{-1022}=-2^{-1022}=-2.2250738585072014e-308$$fraction有52位小数,exponent指数表示的取值范围为-1022~1023;因此,完全可以通过乘以一个幂,将实部转化为整数,得到一个安全的整数(不损失精度),其取值范围:$$\rm[(-1)^0\times(1.\underbrace{000...0}_{52位})_2\times2^{1023-1023},(-1)^0\times(1.\underbrace{111...1}_{52位})_2\times2^{1075-1023}]$$$$\rm[(1)_2\times2^0,(1.\underbrace{111...1}_{52bits})_2\times2^{52}]$$$$\rm[1\times1,(1\underbrace{111...1}_{52位})_2\times2^0]$$$$\rm[1,(\underbrace{111...1}_{53bit})_2\times1]$$$$\rm[1,(\underbrace{111...1}_{53bit})_2]$$$$[1,9007199254740991]$$加号[-9007199254740991,-1]U[1,9007199254740991]当指数全为0(00000000000)时,表示非标准形式的浮点数。从范式浮点数的表达式取值范围可以看出,接近0的值为:$-2^{-1022}$,$2^{-1022}$由此可见,一个实数的整数部分为1,其幂只能无限接近0,但不能表示0。为了解决0值的问题,IEEE针对指标为全的情况确定了另一种计算公式0(00000000000):\(\rm(-1)^s\times(0.\underbrace{bbb...b}_{f(52位)})_2\times2^{e-1022}\)(e=0)s:用符号(0或1)表示的值,可以确定表示值的符号。f:分数小数位,二进制小数位共52位。e:指数值为0,其表达式值为-1022(0-1022)。IEEE754标准规定,非标准形式的浮点数指数的偏移值比普通形式的浮点数的指数偏移值小1。所以公式的指数偏移量是1022,而不是1023。表达式取值范围符号为0,可以使符号为正号;分数的小数位全部为1,可以使实数最大;带入计算得到最大的正数:$$\rm(-1)^0\times(0.\underbrace{111...1}_{52bits})_2\times2^{0-1022}=(0.\underbrace{111...1}_{52bits})_2\times2^{-1022}=2.225073858507201e-308$$与最大正数比较,符号改为负号,得到最小的负数:$$\rm(-1)^1\times(0.\underbrace{111...1}_{52bits})_2\times2^{0-1022}=-(0.\underbrace{111...1}_{52bits})_2\times2^{-1022}=-2.225073858507201e-308$$符号为0,可以使符号为正号;小数最后一位为1,可以使实数成为最小的正数;带入计算得到最小的正数:$$\rm(-1)^0\times(0.\underbrace{000...1}_{52bits})_2\times2^{0-1022}=(0.\underbrace{000...1}_{52位})_2\times2^{-1022}=1_2\times2^{-1074}=2^{-1074}=5e-324$$sign为1,可以使sign为负号;分数的最后一位是1,可以使实数是最小的正数;带入计算得到最大的负数:$$\rm(-1)^1\times(0.\underbrace{000...1}_{52bits})_2\times2^{0-1022}=-(0.\underbrace{000...1}_{52位})_2\times2^{-1022}=-1_2\times2^{-1074}=-2^{-1074}=-5e-324$$sign为0,可以使sign为正号;分数的小数位全部为0,可以使实数为0;带入计算,得到0:$$\rm(-1)^0\times(0.\underbrace{000...0}_{52bits})_2\times2^{0-1022}=0\times2^{-1022}=0$$sign为1,可以使sign为负号;分数小数位是0,可以使实数为0;带入计算得到-0:$$\rm(-1)^1\times(0.\underbrace{000...0}_{52bits})_2\times2^{0-1022}=-0\times2^{-1022}=-0$$总结了标准化形式的浮点数和非标准形式的浮点数所表达的取值范围。综上所述,Number类型的表示取值范围为:[-1.7976931348623157e+308,-5e-324]U0U[5e-324,1.7976931348623157e+308]大于1.7976931348623157e+308为无穷大,小于-1.7976931348623157e+308在[2,48623157e+3085e-324]集合中的数字显示为0安全整数(不损失数字精度)表达式取值范围:[-9007199254740991,9007199254740991]数字类型存在先天缺陷,所以如果对数值的表达和计算有要求,推荐使用BigNumber工具库。