0.1+0.2 为什么不等于 0.3

第一部分

早些年在学 js 的时候这个问题确实碰到过,最后也只是取整而已,知道有这么回事从来不去思考为什么,因为毕竟是非科班出身。但是最近在学 c++的时候,和这个问题杠上了,倒不是因为会阻塞学习流程,主要还是想弄懂;

回答这个问题之前首先我们等学习下计算机存储的模式;很简单不要怕;

计算机在硬件上存储数据的时候,大家都知道是二进制 1010101010,这样;

比如说每个字节是 8 位,int 类型占 4 个字节,也就是 32 位精度;那么 32 位的计算机精度可以存 2 的 32 次方个数据;如下图:

image

每个位上面可以放两个二进制数值也就是 0 或者 1;一般最高位上是符号位(1 表示负数,0 表示正数),所以带符号类型数据应该是 31 个 2

2 _ 2 _ 2 _...... _ 2(31 个 2) ,加上符号范围就是-2147483648 ~ 2147483647;当然也有无符号整型,暂不讨论;

好理解吧;

继续,那么小数怎么存呢,小数在计算机当中叫浮点型,JS 最终会由浏览器引擎转成 C++,但是 js 当中只有一种数值类型,那就是 number,那么 number 在 c++是什么类型呢;

我们暂且认为它是双精度类型,也就是 double,c++中占四个字节,也就是 64 位存储;整数存储参考上面就行;重点说说浮点存储;

同样 64 位可以分为三部分;

什么是 IEEE 754?

第一部分:符号位(S),占 1 位即第 63 位;

第二部分:指数域(E),占 11 位即第 52 位到 62 位,含 52 和 62;

第三部分:尾数域(F),占 52 位即第 0 位到 51 位,含 51;

img

现在我要把一个小数变成二进制 64 位怎么表示呢?比如说 12.52571;

先转换成二进制(十进制如何转换二进制)是

1100.100001101001010011101110001110010010111000011111 =>

1.100100001101001010011101110001110010010111000011111 * 2^3 (小数点向左偏移了三位)(为什么要偏移呢?)

得出结论(IEEE 754 标准)

1、因为是正数,所以符号位 S 是 0;

2、因为向左偏移了三位,所以 E = 1023 + 3 = 1026 (转化为二进制)=> 10000000010 ,有 11 位,不够前面补 0;(为什么要加 1023 呢?为什么左移是加 3,不是减 3?)

3、尾数是(F)(小数点后面)100100001101001010011101110001110010010111000011111;

最终表示: 0 10000000010 100100001101001010011101110001110010010111000011111;

上面总长度是 63 位,差一位,最后面补零,即

0 10000000010 1001000011010010100111011100011100100101110000111110;

那么 12.52571 的 64 位计算机存储形式就是上面了;

第二部分

上面肯定有些问题,大家会迷惑,暂且不表;那么 0.1 和 0.2 大家就知道怎么转了;

但是这里有个问题,0.1 和 0.2 转成二进制小数点后面是循环的;

 // 0.1 转化为二进制
0.0 0011 0011 0011 0011...(0011无限循环)
// 0.2 转化为二进制
0.0011 0011 0011 0011 0011...(0011无限循环)

由于尾数只有 52 位(52 位之后的被计算机截掉了)

E = -4; F =1001100110011001100110011001100110011001100110011010 (52位)
E = -3; F =1.1001100110011001100110011001100110011001100110011010 (52位)

要让两个数相加,首先 E 需要相同,于是得出下面

E = -3; F =0.1100110011001100110011001100110011001100110011001101 (52位) //多余位截掉
E = -3; F =1.1001100110011001100110011001100110011001100110011010 (52位)

上面两个相加得出

E = -3; F = 10.0110011001100110011001100110011001100110011001100111
-------------------------------------------------------------------
E = -2; F = 1.00110011001100110011001100110011001100110011001100111

把第六行转成 10 进制之后就是:0.30000000000000004;

剩余

比如在转的时候还要考虑 E 的取值情况

参考资料

Last Updated:
Contributors: johan