js 单精度浮点数转10进制_老生常谈之js浮点数精度

众所周知,JavaScript 浮点数运算时经常遇到会 0.0000000001和0.99999999这样奇怪的结果,如 1.07*10 = 10.700000000000001。但是,往往当我们遇到问题之后才想起来浮点数对我们造成的负面影响,如果能提前避免,不是更好吗?

常见的几种场景

场景一:进行浮点值运算结果的判断

常见错误写法:floatNum1 + floatNum2 === res

我们在Chrome里测试一下 0.1 + 0.2 === 0.3,得出的结果是false,而不是预期结果true,因为 0.1 + 0.2 === 0.30000000000000004

场景二:将小数乘以10的n次方取整(比如将元转化成分)

常见错误写法:parseInt(yuan*100, 10)

我们在Chrome里测试一下 parseInt(0.58*100, 10),得出的结果是57,而不是预期结果58。

场景三: 四舍五入保留n位小数

常见错误写法: (number).toFixed(2)

我们在Chrome里测试一下 (1.335).toFixed(2) ,得出的结果是1.33,而不是预期结果1.34。

可以发现,稍有不注意,浮点数运算就会出问题,那么我们如何解决、防范以上问题呢?

常见解决方案

因为大家一旦察觉到浮点数精度的负面影响,就会马上想到不同思路的解决方案,因此本文的目的是希望大家引起对浮点数的重视,尤其是在计算金额这种需要高严谨数据的枪框下,而非重点介绍解决方案,所以,这一部分就不详细展开了。

大概就是将浮点数转化成字符串,通过String.split取出小数点前后的数据再做特殊处理。当然也有现成的库可供选择,如 number-precision。

那么下面我们详细了解一下为什么会出现这种奇怪的现象呢?

浮点数的存储

JavaScript 中所有数字包括整数和小数都只有一种类型 — Number。它的实现遵循 IEEE 754 标准,使用 64 位固定长度来表示,也就是标准的 double 双精度浮点数(相关的还有float 32位单精度)。

这样的存储结构优点是可以归一化处理整数和小数,节省存储空间。64位比特又可分为三个部分:符号位S:第 1 位是正负数符号位(sign),0代表正数,1代表负数

指数位E:中间的 11 位存储指数(exponent),用来表示次方数

尾数位M:最后的 52 位是尾数(mantissa),超出的部分自动进一舍零

实际数字就可以用以下公式来计算:

注意以上的公式遵循科学计数法的规范,在十进制中 0

不知道怎么转换的同学可以查看 二进制十进制间小数怎么转换 。

用二进制对应的科学计数法就是 1.001*2^2,舍去1后M=001。

因为指数位E有 11 位,是一个无符号整数,取值范围是 0 到 2047(2047 = Math.pow(2,11)-1)。但是科学计数法中的指数是可以为负数的,所以约定减去一个中间数 1023,[0,1022] 表示为负,[1024,2047] 表示为正。如 4.5 的指数 E = 1025(1025 = 1023+2),尾数 M = 001。

最终的公式变成:

所以 4.5 最终表示为(S=0、M=001、E=1025)

4.5二进制

下面再以 0.1 为例解释浮点误差的原因,0.1 转成二进制表示为 0.0001100110011001100(1100循环),1.100110011001100x2^-4,所以 E=-4+1023=1019;M 舍去首位的1,得到 100110011...。最终就是:

0.1二进制

转化成十进制后为 0.100000000000000005551115123126,因此就出现了浮点误差。

那么下面我们再回到应用场景中的例子,通过二进制数据查看究竟为什么会出现问题:

为什么0.1+0.2=0.30000000000000004?

// 0.1 和 0.2 都转化成二进制后再进行运算

0.00011001100110011001100110011001100110011001100110011010 +

0.0011001100110011001100110011001100110011001100110011010 =

0.0100110011001100110011001100110011001100110011001100111

// 转成十进制正好是 0.30000000000000004

为什么(1.335).toFixed(2)=1.33?

因为1.335其实是1.33499999999999996447286321199,toFixed虽然是四舍五入,但是是对1.33499999999999996447286321199进行四五入,所以得出 1.33。

小结

关注浮点数计算,防患于未然。

你可能感兴趣的:(js,单精度浮点数转10进制)