js计算精度问题

在用js做计算的时候,经常会出现下面的这些问题:

0.1 + 0.2 = 0.30000000000000004
0.3 - 0.2 = 0.09999999999999998
20.123/100 = 0.201230000000000000000002
2.425*100 = 2.424999999999997

IEEE 754 64 位浮点类型
IEEE 754

IEEE 754 规定了四种表示浮点数值的方式:单精确度(32位)、双精确度(64位)、延伸单精确度(43比特以上,很少使用)与延伸双精确度(79比特以上,通常以80位实现)。

该标准的全称为IEEE二进制浮点数算术标准(ANSI/IEEE Std 754-1985),又称IEC 60559:1989,微处理器系统的二进制浮点数算术(本来的编号是IEC 559:1989)。
单精度浮点数
单精度浮点数格式是一种数据类型,在计算机存储器中占用 4 个位元(32 bits),利用“浮点”(浮动小数点)的方法,可以表示一个范围很大的数值。

在 IEEE 754-2008 的定义中,32-bit base 2 格式被正式称为 binary32 格式。这种格式在 IEEE 754-1985 被定义为 single,即单精度。需要注意的是,在更早的一些计算机系统中,也存在着其他 4 字节的浮点数格式。

定义

第 1 位表示正负,中间 8 位表示指数,后 23 位储存有效数位(有效数位是 24 位)。

中间八位共可表示 28=256 个数,指数可以是二补码;或 0 到 255,0 到 126 代表-127 到-1,127 代表零,128-255 代表 1-128。

有效数位最左手边的 1 并不会储存,因为它一定存在(二进制的第一个有效数字必定是 1)。换言之,有效数位是 24 位,实际储存 23 位。

js计算精度问题
双精度浮点数
双精度浮点数(double)是计算机使用的一种数据类型。比起单精度浮点数,双精度浮点数(double)使用 64 位(8 字节) 来存储一个浮点数。 它可以表示十进位制的 15 或 16 位有效数字,其可以表示的数字的绝对值范围大约是 [2.23e-308,1.79e308]

定义

和单精度类似,第 1 位表示正负,后 11 位为指数位,最后 52 位表示精确度(有效位数是 53 位)。

js计算精度问题

0.1/0.2的二进制位

(0.1).toString(2) === "0.000 110011001100110011001100110011001100110011001100110 1“

(0.2).toString(2) === "0.00 1100110011001100110011001100110011001100110011001101"

(0.30000000000000004).toString(2) === "0.0100110011001100110011001100110011001100110011001101"

(0.3).toString(2) === "0.010011001100110011001100110011001100110011001100110011"

所以最终 0.1 用二进制表示是 0.0001 1001 1001 1001 ...,但是我们看上面 (0.1).toString() 最后的六位 001101,正常循环应该是 001100,所以截断之后,0.1 二进制表示的值变大了!!!。0.2 转换为二进制表示截断之后也变大了。

通过对比 0.1、0.2 及它们的和的二进制表示,可以发现字符串的长度变化了,但是精确度却没有变化,也就是从 1 开始到最后的字符串长度都是 52。

0.1 + 0.2 本来应该是长度在为 57,但是由于无法表示这样一个数,重新从 1 开始的数字开始计数,会截断最后的三个数字 (最后精确度为 52 或者 53 )

进行2进制位的加法后0.1+0.2为
0.0100 1100 .......... 1100 111 总共57,然后从第一个1算起有54,但是最大也就是53位,会进行截断成52或者53然后就变大了

需要注意一下不同的小数最后保存的位数不一样。

具体解决方法建议用库解决,然后去研究库的解决方案。
一个比较通用的方案是把小数转化成整数运算之后再变回小数来解决!
math.js bigNumber.js decimal.js number-precision 都可以用来解决

猜你喜欢

转载自blog.51cto.com/13934921/2569895