double 和float精度问题解析

709 阅读3分钟

在Java核心技术卷或者C++ Pprmer Plus中,有一句话,float占有32个有效位,又有一句,float至少占有6个有效位;这句话是什么意思?

double 和float 为什么一个数双进度,一个是单精度?为什么这种类型的数据精度会存在丢失的问题?我来试着解释一下。

using namespace std;

int main(void)
{
    cout.setf(ios_base:fixed,ios_base::flaotfield);
 	double tes = 10.0/3.0;
    float tms = 10.0/3.0;
    
    cout << "tes:" << tes << endl;
    cout << "tms:"	<< tms << endl;
    
    const float Million = 10E6;
    cout << "tes million is:" << tes * Million << endl;
    cout << "tms million is:" << tms * Million << endl;
    
    return 0;
}

首先,double 和 float 类型修饰的变量的值,存储在内存中是按照二进制来存储的,而float 作为单精度的数据类型,存储的是8个字节,一共三十二位二进制数据,而double作为双进度数据类型,存储的是64位二进制数据,也就是16个字节的长度。
举个例子

double i = 2 存储在内存中就是 100010000....0;(32位)

这些位数中,第一位是符号位,1代表负数,2代表正数,后面8位代表指数位,我们可以把十进制的数据转换为二进制,以科学计数法表示,8位代表的就是指数位的二进制表示,后面的就是小数位,小数位后面的不足32位的用0填充;

举个例子: 8.25 这个是10进制的,转换为2进制就是 1.00001 * 2e3

---太长不看系列---

tips : 解释: 8.25 拆分为整数部分 8 和小数部分0.25 ,其中8 的二进制表示就是1000,0.25表示成十进制就是01;(十进制转换为二进制整数/2,小数需要乘以2 一致乘到小数位为0为止);所以8.25 的二进制表示就是 100001 用科学记数法表示就是1.00001 * 2e3;

---太长不看结束系列---

符号位(1位)                                指数位(8位)                                小数位

0                                                        127+3=130                                    00001

0                                                        1000 0010                                     00001

---太长不看系列--

指数位占八位,就是0-255,我们知道指数有正整数和负整数,所以我们按照0-126 代表负整数,127-255代表正整数,二进制的指数位是3,代表的就是在127的基础上,加上3的指数位;

-- 太长不看系列结束---

所以存储在内存中的值就是0100000100001000000..0不足的位数补0;

那么我们解释的第一个问题,float占有32个有效位,又有一句,float至少占有6个有效位;

第一句,float占有32个有效位,代表的是占了32个二进制有效位 而一个字节存储8个二进制,而一个十进制存储2个十进制,就先当于,1个十进制代表可以换算为4个二进制数据,那么我们float代表的32个有效位就相当于十进制的6个有效位;

第二,double 和float 为什么一个数双进度,一个是单精度?

double 里面存储的位数是64位,等于相当于十进制中的12个有效位,而float存储的数据是6个有效位。所以float存储的是双进度,而float是单精度;

第三个,为什么在计算过程中会存在精度丢失的问题?

我们举得例子就是0.25的,所以我们在小数位转换为2进制数的时候啊,两部就能到达1的结果,但是如果我们的例子换成1.17,这个结果就无限接近于1,但是不能到达1这个结果;

所以存在内存中的数据,就是一个近似值,所以进度肯定会存在丢失的问题;