组成原理|为何计算机中0.3 + 0.6 等于 0.899999999...?

浮点数的不精确性

打开浏览器控制台 输入 0.3 + 0.6 ,结果输出了0.8999999,不相信你能够按F12打开控制台试一试O(∩_∩)O
java

这是为何?
咱们先看下面的介绍,最后文末会给出答案。算法

定点数的表示

使用BCD编码:用二进制来表示十进制的编码方式浏览器

编码过程:咱们用 4 个比特来表示 0~9 的整数,那么 32 个比特就能够表示8个这样的数
而后咱们把最右边的 2 个 0~9 的整数,当成小数部分;
把左边 6 个 0~9 的整数,当成整数部分。
这样,咱们就能够用 32 个比特,来表示从 0 到 999999.99 这样 1 亿个实数了。编码

适用途径:银行、商家精确到“分0.01 ”的交易。
缺点:浪费比特啊,并且这样的表示方式没办法同时表示很大的数字和很小的数字。code

浮点数的表示

IEEE的标准,它定义了两个基本的格式:float单精度 和 double双精度blog

下面是单精度的表示方法:ip

1.符号为表示正负0和1。
2.指数位,由于咱们也须要表示很小的数,指数位置须要负数,因此咱们在这里用 1~254 映射到 -126~127 这 254个有正有负的数
3.有效数位,是一个 23 个比特组成的有效数位。咱们用f来表示。get

举了栗子:
数学

浮点数相加的精度偏差

栗子1:让一个值为 2000 万的 32 位浮点数和 1 相加,你会发现,+1 这个过程由于精度损失,被“彻底抛弃”了。it

float a = 20000000.0f;
float b = 1.0f;
float sum = a + b;
print sum //发现sum的值仍是2000万,而不是 200000001

为何出现上述状况,由于两个浮点数相加的运算是须要移位的,
过大或者太小的数移位以后可能会出现有效位数消失的状况,此部份内容能够搜寻相关资料:浮点数相加运算过程、浮点数的二进制表示

栗子2:将一个等于1.0的浮点数累加循环加2000万次,发现结果是1600万左右,你能够用java实现下面代码,看看结果~~

为何?也是浮点数加法产生的精度偏差

float sum = 0.0f;
    for (int i = 0; i < 20000000; i++) {
        float x = 1.0f;
        sum += x;       
    }
    print sum // sum 约等于1.6777216E7

解决方案:Kahan Summation 算法

float sum = 0.0f;
    float c = 0.0f;
    for (int i = 0; i < 20000000; i++) {
        float x = 1.0f;
        float y = x - c;
        float t = sum + y;
        c = (t-sum)-y;
        sum = t;        
    }
   print sum;//2000万

在每次的计算过程当中,都用一次减法,把当前加法计算中损失的精度记录下来而后在后面的循环中,把这个精度损失放在要加的小数上,再作一次运算。
该算法的数学证实参见:Wikipedia 连接

因此回到文章开头的问题,0.3 + 0.6 为何不等于0.9,
就是由于计算机用浮点数表示法来表示浮点数,只能精确表示 2^x (2的x次方)这种数0.三、0.6不能精确表示出来,例如0.5是2^(-1)是能够被精确表示的。

相关文章
相关标签/搜索