我们都知道计算机存储数值都是采用的是二进制,在IEEE754标准中,规定了四种表示浮点数值的方式:单精度(32位)、双精度(64位)、延伸单精确度、延伸双精确度。而Javascript中的数据基本类型只有Number为数值的,Number遵循的是IEEE754规范的,采用的是双精度存储的。
符号位s:0表示正,1表示负。阶码e:浮点数的幂次,一般采用移码(64位:2^(e-1023),e的范围:[0,1023];32位:2^(e-127),e的范围:[0,127])表示,当阶码为固定值时,存储的数称为定点表示;当阶码为可变时,存储的数称为浮点表示。尾数M:浮点数的小数部分,数值的有效数字。
浮点数规格化分类:对指数部分二进制进行规格化,这里以64位为例,32位也是同理的。
-
规格化:11位指数不全为00000000000和11111111111,范围在00000000001~11111111110(1~2046)内的都是规格化数值。什么是规格化?整数的规格化类似于数学的科学记数法(1.0x10^n),而浮点数的规格化公式如下图,比如小数存储格式为1.Mx10^(-n)
尾数部分是规格化表示的,最高位总是“1”,规格化时计算机会直接隐藏掉,转换的时候会再次加回去的
非规格化:指数部分全为00000000000(0)。特殊值:指数部分全为11111111111,然后分两种情况分析小数部分。- 当52位小数部分全为1时,符号位为0表示
+Infinity(正无穷),符号位为1表示-Infinity(负无穷)。 - 当52为小数不全为1时,表示
NAN(Not a Number)。
- 当52位小数部分全为1时,符号位为0表示
十进制(整数、小数)转化二进制:
-
整数转二进制(除2取余):如12。
-
小数转二进制(乘2取整):如0.25。
浮点数为什么会出现精度问题?
因为计算机中的浮点数对应数学当中的小数,64位浮点数最多可以表示2^64个数(-2^63-1~2^63-1),而在数学中[0,1]中的小数是无穷多个,计算机不能把所有小数都能计算,所以就出现了近似值,从而导致精度损失。比如像1/3、1/5、1/7、1/9、1/10这些分母说代表的小数对应的二进制都是无限循环的,但计算机不能把所有二进制都表示出来,此时就需要舍去不能表示的二进制,我们都知道整数中有四舍五入,而二进制中只有0和1,它进行的是进1舍0。
从32位单精度去分析0.1+0.2的值:0.1与0.2的二进制都是无限循环小数,所以无法精确显示,只能获取到一个近似值,
0.1二进制规格化浮点数:1/16 × 1.6000000238418579 = 0.1000000014901;
0.2二进制规格化浮点数:1/8 × 1.6000000238418579 = 0.200000002982;
32位单精度浮点数计算:0.1+0.2 = 0.3000000044721;从这里我们可以看到这里的数与控制台计算的不一样,因为JavaScript使用的是双精度存储数值的,这里的小数无法用二进制描述详尽。
如何解决浮点数丢失问题?
-
Number.prototype.toFixed(x):保留小数后面x位数。
console.log(.1 + .2);// 0.30000000000000004 console.log((0.1 + 0.2).toFixed(1));// 0.3 -
math.js:math.js是JavaScript和Node.js的一个广泛的数学库。支持数字,大数,复数,分数,单位和矩阵等数据类型的运算。
const result = math.evaluate('0.1 + 0.2') // 注意:format参数precision 0-16代表精度定义了总数返回的有效位数,默认为0 console.log(math.format(result)) // 0.30000000000000004 console.log(math.format(result, 14)) // 0.3 console.log(math.format(math.add(math.bignumber(0.1), math.bignumber(0.2)))) // 0.3 -
decimal.js:提供JavaScript的任意精度Decimal类型。
const Decimal = require('decimal.js') const a = Decimal.add(0.1, 0.2) const b = new Decimal(0.1).add(0.2) console.log(a) // 0.3 console.log(b) // 0.3 -
bignumber.js:一个用于任意精度算术的JavaScript库。
const BigNumber = require('bignumber.js') console.log(new BigNumber(0.1).plus(0.2).toString()); // 0.3 -
Big.js(
推荐):一个用于任意精度十进制算术的小型、快速、易于使用的库。const Big = require('big.js') console.log(new Big(0.1).plus(0.2).toString()) // 0.3