由toFixed再谈计算机对数字的存储

160 阅读2分钟

先看以下这种场景

image.png

再看MDN文档对toFixed的描述

image.png

看到这里我会有一个疑问,为啥2.45和2.55四舍五入都是等于2.5

这就再次说道计算机里数字的运算是不精确的

image.png

这种不精确的计算主要出现在三个方面

1)存储

2)运算

3)显示

这三种情况都有可能导致计算不精确

1)存储,把一个十进制的数转成为二进制后可能会出现无限循环

比如:

image.png 但是计算机存储能力有限,它有个固定的精度到了精度之后后面的数字就不要了,但是这里也不是简单的舍去而去进行舍入

比如:

0.001100 1100110011

假如这个二进制的精度就是到加粗的这几个数字,那么他在截取的时候会看后面一位,如果后面一位是1那么就会往前进1,如果是0,最后一位就会填0

所以我们看0.2的二进制最后一位是1,所以这里会导致0.2在计算机中的存储结果实际上是比0.2大的

image.png

(toPrecision() 方法以指定的精度返回该数值对象的字符串表示,四舍五入到 precision 参数指定的显示数字位数。)

由于存储是不精确的这就会导致一个奇怪的现象

image.png

 

2)运算,计算机使用存储的二进制进行运算的

看下图:

image.png

 

这就很奇怪了,0.2是不精确的,所以0.2+0.1不精确这很好理解

但是为什么+0.3就能得到精确的0.5?

大概原因:是因为两个不精确的数字相加可能会出现相互抵消,也就是一个偏小的值加一个偏大的值相加刚好抵消的误差,0.3精确到后面20位小数是偏小的,0.2精确到后面20位小数是偏大的

0.1与0.2都是偏大的,所以相加也是偏大的

 

3)显示,我们知道0.2在计算机中是不精确的那么为什么直接打印出来是精确的呢

image.png

这是因为浏览器在显示的时候会做一个近似处理,它发现0.2的2进制像0.2并且误差比较小,那么它就会显示0.2,其实这个是不精确的,精确的显示应该是0.2000000000000004,0.1+0.2不做近似处理是因为相加以后误差被放大,它无法确定是不是0.3,所以没做近似处理

 

接下来我们再看toFixed,toFixed的实现原理是先运算再显示,由于我们的运算与显示都是不精确。

image.png

 

 

2.45是不精确的,我们在把2.45与2.55精确到100位

  image.png

toFixed是对上面的值进行四舍五入的,所以2.45.toFixed(1)四舍五入为2.5,2.55.toFixed(1)四舍五入也是2.5

 

解决方案:使用第三方库decimal.js

www.npmjs.com/package/dec…

 

tofixed具体计算方法可以看下面文档(我看不懂)

262.ecma-international.org/13.0/#sec-n…