0.1+0.2 ≠ 0.3(小朋友,你是否有很多问号?)

312 阅读3分钟

image.png 小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

不要吹灭你的灵感和你的想象力; 不要成为你的模型的奴隶。 ——文森特・梵高 (打开了掘金控制台看到的一句话)

正文

从小学时候,我们都知道0.1+0.2=0.3是很常见的一道算术题,等做了程序员,才发现,在代码中,0.1+0.2≠0.3。打开控制台,输入这道题,就看到了结果:

image.png

解惑

这道题无论是在js中还是python中,结果都不等于0.3,为什么呢?

要想彻底弄清楚这个问题,先要弄清楚浮点数在计算机中是怎么存储的?

在大学里我们学到(毕业就还给学校了),在计算机中,浮点数是用科学计数法存储的,不管数值多大,都是计数到小数点后一位,比如0.0314=0.314E-1,前面一位是符号为,后跟指数,再跟数值。

IEEE标准定义了2个基本的浮点数格式,一个是32位的单精度浮点数,一个是64位的双精度浮点数,也就是floatfloat32doublefloat64这两个数据格式,单精度和双精度的表示形式差不多,以单精度为例,分为三部分:

  1. 第一部分是符号位,用s表示,代表正负,要记住的是在浮点数的范围内,所有数字都是有符号的;
  2. 第二部分是指数位,用e表示,代表指数,用8位bit表示的数字范围是0 ~ 255,为了同时表示大数和小数,我们把0 ~ 255去掉头尾(0,255后面会用到)的1 ~ 254去映射到-126~127,这样同时可以表示最大最小数字;
  3. 第三部分是有效数位,用f表示,代表的是有效的数位;

image.png

综合上述表示和科学计数法,我们的浮点数就可以表示为公式
(-1)^s * 1.f * 2^e 这个公式是不能表示0的,当e和f为0的时候,我们就认为这个浮点数为0。

以0.5为例,0.5的符号位s是0,f也是0,e是-1,这样(-1)^0 * 1.0 * 2 ^ -1 = 0.5,那0.1是怎么表示的呢: (-1)^s * 1.f * 2^e = 0.1,这里s=0,f=0,那么

e = Math.log2(0.1) // -3.321928094887362

从0.1到0.9只有0.5是可以求出一个准确的值的,剩下的都算不出来一个准确的值,这也就是为什么0.1 + 0.2会导致的精度问题,也就是说浮点数无论是表示还是计算其实都是近似计算,而近似计算就一定会导致一些问题。

尾声

就当我刚刚觉得自己好像弄懂了计算机组成原理的一丢丢知识的时候,我发现0.1+0.1=0.2

image.png

计算机内部是在嘲笑我吗~

可是我才不会和你一般见识的,我猜想计算机内部对于计算就是一个计算近似值的过程,如果在一个近似范围内,就会没有误差,超过这个范围就会出现误差了(等待继续钻研~)