关于float浮点型数据类型的精度问题,首先,我们举这样一个例子:

#include<stdio.h> 

int main() 

{ 

 float a = 123456789; 

 printf("%f\n",a); 

        return 0; 

 }



定义一个float浮点型数据类型的a,赋值为123456789,编译后运后,结果却为:123456792.000000

为什么会出错,错在哪里呢?想要彻底搞懂这个问题,我需要解决:
1.float的范围多少?
2.float精度是多少? 
3.float在内存中如何存储?
4.在存储中哪里出了错?
5.对于这种问题应该怎么解决?

浮点类型变量由尾数(包含数字的值)和指数(包含数字的数量级)表示。

1.float的范围多少?
float类型数据占4字节内存(4B),共32位(32bit),其中:1bit(符号位),8bit(指数位),23bit(尾数位)。
所以,float的指数范围为-127~+128。其中负指数决定浮点数所能表达的绝对值最小的非零数;而正指数决定浮点数所能表达的绝对值的最大的数,也决定浮点数的取值范围。
float的范围:-2^128~2^128,即-3.4E+38 和 3.4E+38 之间的范围。

2.float精度是多少?
float的精度是由尾数的位数决定的。浮点数在内存中是按科学计数法来存储的,其整数部分始终隐藏着一个“1”,由于它是不变的,故不能对精度造成影响。
float:2^23=8388608,一共七位,所以一共最多七位有效数字,但绝对能保证的为6位。故:float的精度为6~7位有效数字。

3.float在内存中如何存储?
计算机中存储二进制的科学计数法,下面举例:
120.5(十进制)-->111 0110.1(二进制)-->1.110 1101*2^6
任何二进制数的科学计数法均能表示为:1.xxxxx*2^n
所以,第一位的“1”可省略,23bit的尾数部分可以表示的精度为24bit

又因为:9(十进制)-->1001(二进制),

即4bit可以精确到十进制小数点后1位,

所以,24bit(4*6)可以精确到十进制小数点后6(1*6)位。



而指数部分,可正可负


指数部分存储采用移位存储


120.5-->1.110 1101*2^6


6+127 = 133


133(十进制) --> 1000 0101



所以:120.5在计算机中存储为:


0(符号位)  1000 0101(指数位) 110 1101 0000 0000 0000 0000(尾数位)



4.在存储中哪里出了错?


疑问,123456789 < 2^128,


  所以为什么float a = 123456789打印错误呢? 


答案:误差(老师原话)!因为精度不够导致的误差。


下面我们探究23456789怎么存储:

123456789(十进制)


   |转


   |换


110 0101 1011 1100 1101 0001 0101(二进制)


   |科学


   |计数法


1.10 0101 1011 1100 1101 0001 0101*2^26


也就是尾数位为26位


前面说过float尾数部分可以表示的精度为23bit,最多为24bit



※※※※※※※※※※※※※※※※※※※※※※


(疑问 这里到底是23还是24??)


(经过验证,123456792的二进制的科学计数法 尾数位23位。)


答案:就是23!23是一定能够确定的值。


※※※※※※※※※※※※※※※※※※※※※※



1.10 0101 1011 1100 1101 0001 0101*2^26


后四位为:0101


省去后三位需要进1位 为:1000

(这里的“进位”是本人猜测的解释,没有得到老师的认可。具体float在内存中的存储方式太过复杂,这里的进位说,有助于理解(自圆其说),不然123456789—>123456792。)

1.10 0101 1011 1100 1101 0001 1000*2^26

1.10 0101 1011 1100 1101 0001 1*2^23

   |转


   |换


123456792(十进制)



5.对于这种问题应该怎么解决? 


答:一:设置精度!二:修改数据类型为double



下面举例一设置精度:一个二元一次方程求根函数(a、b、c为方程系数)



#include <stdio.h>


#include <float.h>


#include <math.h>



#define EPS 0.000001


/*


(疑问 可以定义为0.001吗?)


答案:可以。精度自己定义。

*/ 


void Fun(double a,double b,double c) 

{ 

 double x1; 

 double x2; 

 double d = b*b - 4*a*c; 

 if(-EPS<=a && a<=EPS) 

 { 

 x1=x2 =-c/b; 

 printf("x1=%f,x2=%f\n",x1,x2); 

 } 

} 


#define EPS 0.000001表示定义一个精度0.000001 

if(-EPS<=a && a<=EPS)表示if(-0.000001<=a 并且a<=0.00001), 

即if(a=0) 


下面举例二:#include<stdio.h> 

int main() 

{ 

 double a = 123456789; 

 printf("%f\n",a); 

        return 0; 

 }