float浮点型数据类型精度问题探究

关于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;
 } 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值