zoukankan      html  css  js  c++  java
  • 100个0.1相加等于多少?

    一、前言

      在大家的认知过程中可能会认为计算机是不会出现计算错误的,但是实际上,依然存在程序运行后无法得到正确数值的情况。其中,最经典的就是小数运算。(做金融的一定要小心!!!

    二、引入

      在我们的世界里面,100个0.1相加就是10,这个是没有疑问的。但是当我们用C语言如下的程序来计算的时候,结果并非是10(不同语言计算的结果可能不同,这里主要说C)。

      首先是一段计算代码:

    #include <stdio.h>
    
    int main(void) { 
          float sum;
            int i;
            sum = 0;
            for (i=0 ;i<100;i++) {
                sum += 0.1;
            }
            printf("%f
    ",sum);
    }

    运行结果如下:

    10.000002

    计算机通过编译、链接、运行得到的结果是10.000002。程序没有错。现在让我们来看一下具体原因吧。

    三、计算机计算结果不正确的原因

      简单来说,就是无法表示正确的数值,导致计算出来的结果成了近似值。下面进一步剖析一下。

      首先,我们来看一下在计算机世界里面如何用二进制数表示小数:

      例如把1011.0011这个小数点的二进制数转成十进制数。(只需将各数位数值和位权相乘,然后将相乘的结果相加)

      也就是:1*2^3+0*2^2+1*2^1+1*2^0+0*2^(-1)+0*2^(-2)+1*2^(-3)+1*2^(-4) = 11.1875。

      了解了二进制表示的小数转十进制的方法后,计算出错的原因也就容易理解了。用小数点后4位用二进制表示时的数值范围为:0.0000~0.1111。因此,对应的十进制结果如下:

    从上面的对照表可以看出,0的下一位就是0.625。因此0~0.0625之间的数值计算机无法用小数点后4位数的二进制数表示。因此可以看出0.1无法用4位二进制数表示。就算增加二进制的位数,也无法得到2^(-x) =0.1 这个结果。

      实际上,十进制0.1转成二进制后,就变成了0.0001100110011……(1100循环)这样的循环小数。就像1/3是一个道理。因此100各0.1相加不等于10,而是等于近似值。

    ---------------------------------------------以上就能够回答标题的原因了---------------------------------------------

    四、What is 浮点数?

      其实像刚才那样的1011.0011这种表现形式完全是纸面上的二进制数表现形式,在计算机内部是无法使用的(计算机内部只是0101001……没有"."这个概念)。实际上,编程语言提供了双精度浮点数(double)和单精度浮点数(float)。双精度浮点数类型用64位、单精度浮点数用32位来表示全体小数。

      浮点数:就是用符号、尾数、基数和指数表示的小数。

      

    其中:±表示符号,m表示尾数,n表示基数,e表示指数。实际数据中不考虑基数。因此:

    其中:

    1、符号部分:1表示负、0表示正或者0。

    2、尾数部分用的是:将小数点前面的值固定位1的正则表达式。

    3、指数部分:用的是EXCESS系统表现。

      

      先看看尾数部分。对于十进制的0.75。我们有如下的表示方法:

      ①、0.75 = 0.75*10^0

      ②、0.75 = 75*10^(-2)

      ③、0.75 = 0.075*10^1

      十进制的表示正则为:小数点前面是0,小数点后面第一位不是0的规则表示。而对于二进制也是一样的道理,使用的是:将小数点前面的值固定为1的正则。也就是将二进制数表示的小数左移或右移(逻辑移位)数次后,整数部分的第一位变成1,第二位之后变成0.而且第1位的1在实际数据中不保存。

      例如1011.0011:

      移位变成0001.0110011,确保小数点后23位:0001.01100110000000000000000,仅保留小数点后面完成正则:01100110000000000000000。

      再看看指数部分。EXCESS系统表现:将指数部分表示范围的中间值设置为0,使得负数不需要用符号来表示。例如当指数部分是8为单精度浮点时,最大值11111111=225的1/2即01111111=127表示0。双精度类似。

      因此对于单精度浮点数的表现,其表示范围就是:00000000~11111111也就是-127~128。看下面例子:

    #include <stdio.h>
    #include <string.h>
    int main(int argc, char *argv[]) {
        float data;
        unsigned long buff;
        int i;
        char n[34];
        //将0.75以单精度浮点数形式存储在data中
        data = (float)0.75;
    
        memcpy(&buff,&data,4);
        for (i=33;i>=0;i--) {
            if(i==1 || i==10) {
                n[i] = '-';
            }else {
                if(buff%2==1) {
                    n[i] = '1';
                }else {
                    n[i]='0';
                }
                buff/=2;
            }
            
        }
        n[33] = '';
        printf("%s
    ",n);
    
    }

    运行结果:

    0-01111110-10000000000000000000000

    其中01111110是126,EXCESS表示为-1。

    小数点前面的第一位是1。因此尾数就是:1.10000000000000000000000也就是1.5。

    也就是+1.5*2^(-1) = 0.75。

    五、如何避免小数计算出错导致的问题

      可以将小数替换成整数来计算。然后在缩小相应的倍数。

    注:

      1、如果有什么Bug或者说的不对的地方,欢迎大家随时提建议或者意见。 

  • 相关阅读:
    导包路径
    django导入环境变量 Please specify Django project root directory
    替换django的user模型,mysql迁移表报错 django.db.migrations.exceptions.InconsistentMigrationHistory: Migration admin.0001_initial is applied before its dependen cy user.0001_initial on database 'default'.
    解决Chrome调试(debugger)
    check the manual that corresponds to your MySQL server version for the right syntax to use near 'order) values ('徐小波','XuXiaoB','男','1',' at line 1")
    MySQL命令(其三)
    MySQL操作命令(其二)
    MySQL命令(其一)
    [POJ2559]Largest Rectangle in a Histogram (栈)
    [HDU4864]Task (贪心)
  • 原文地址:https://www.cnblogs.com/zhanggui/p/5699432.html
Copyright © 2011-2022 走看看