C语言中float和double的精度

助教给小伙伴们调实验的时候,碰到一个求矩阵面积交的问题,问题如下,并不复杂。

问题描述
 平面上有两个矩形,它们的边平行于直角坐标系的X轴或Y轴。对于每个矩形,我们给出它的一对相对顶点的坐标,请你编程算出两个矩形的交的面积。
输入格式
 输入仅包含两行,每行描述一个矩形。 
 在每行中,给出矩形的一对相对顶点的坐标,每个点的坐标都用两个绝对值不超过10^7的实数表示。 
输出格式
 输出仅包含一个实数,为交的面积,保留到小数后两位。
 样例输入
 1 1 3 3
 2 2 4 4 
样例输出
 1.00
我的思路是首先求出两个矩形中心的位置,根据两个矩形中心的位置,结合矩形的长宽(这里实际上用的是长宽的一半),很容易求得矩形相交部分的面积。程序如下:

#include 
#include 
int main()
{
	//分别用abcd和a1b1c1d1来接收矩形的相对顶点坐标
//     double a,b,c,d;
//     double a1,b1,c1,d1;
//     
//     scanf("%lf%lf%lf%lf",&a,&b,&c,&d);
//     scanf("%lf%lf%lf%lf",&a1,&b1,&c1,&d1);
	float a,b,c,d;
	float a1,b1,c1,d1;
 
	scanf("%f%f%f%f",&a,&b,&c,&d);
	scanf("%f%f%f%f",&a1,&b1,&c1,&d1);
	
	//x,y和x1,y1分别表示矩形中心的坐标
    double x=fabs(c-a)/2.0,y=fabs(d-b)/2.0;
    double x1=fabs(c1-a1)/2.0,y1=fabs(d1-b1)/2.0;

	//xx,yy和xx1,yy1分别表示矩形的长宽的一半
    double xx=(a+c)/2.0,yy=(b+d)/2.0;
    double xx1=(a1+c1)/2.0,yy1=(b1+d1)/2.0;

	//xxx和yyy表示两个矩形中心的横纵距离
    double xxx=fabs(xx-xx1),yyy=fabs(yy-yy1);
    if (x1+x<=xxx || y+y1<=yyy)
    {
        printf("%.2f\n",0.000);
    }
    else
    {
        double height=fabs(y1+y-yyy);
        double width=fabs(x1+x-xxx);
		if (height > (y > y1 ? y1*2 : y*2))
			height=(y > y1 ? y1*2 : y*2);
		if (width > (x > x1 ? x1*2 : x*2))
		{
			width=(x > x1 ? x1*2 : x*2);
		}
        printf("%.2lf\n",height*width);
    }
    return 0;
}
结果运行之后,有一组用例总是不过。通过某种不怎么光彩的手段,我知道,那组用例的输入数据如下:

283323.2393 2938832.3994 29838432.38288 983723.828
27783.84384 8793002.2 3995852.3884 2928344.2
没头没脑地把接收输入的变量的类型改成了double,程序如下:

#include 
#include 
int main()
{
	//分别用abcd和a1b1c1d1来接收矩形的相对顶点坐标
    double a,b,c,d;
    double a1,b1,c1,d1;
    
    scanf("%lf%lf%lf%lf",&a,&b,&c,&d);
    scanf("%lf%lf%lf%lf",&a1,&b1,&c1,&d1);
// 	float a,b,c,d;
// 	float a1,b1,c1,d1;
//  
// 	scanf("%f%f%f%f",&a,&b,&c,&d);
// 	scanf("%f%f%f%f",&a1,&b1,&c1,&d1);
	
	//x,y和x1,y1分别表示矩形中心的坐标
    double x=fabs(c-a)/2.0,y=fabs(d-b)/2.0;
    double x1=fabs(c1-a1)/2.0,y1=fabs(d1-b1)/2.0;

	//xx,yy和xx1,yy1分别表示矩形的长宽的一半
    double xx=(a+c)/2.0,yy=(b+d)/2.0;
    double xx1=(a1+c1)/2.0,yy1=(b1+d1)/2.0;

	//xxx和yyy表示两个矩形中心的横纵距离
    double xxx=fabs(xx-xx1),yyy=fabs(yy-yy1);
    if (x1+x<=xxx || y+y1<=yyy)
    {
        printf("%.2f\n",0.000);
    }
    else
    {
        double height=fabs(y1+y-yyy);
        double width=fabs(x1+x-xxx);
		if (height > (y > y1 ? y1*2 : y*2))
			height=(y > y1 ? y1*2 : y*2);
		if (width > (x > x1 ? x1*2 : x*2))
		{
			width=(x > x1 ? x1*2 : x*2);
		}
        printf("%.2lf\n",height*width);
    }
    return 0;
}
程序居然神奇地过了。于是,想到可能是精度问题,调试了一下,记录下各个变量的值如下:

C语言中float和double的精度_第1张图片
大致可以看出,float的精度貌似只能到6位有效数字。google到一篇解释double和float精度这个问题的博客,很详细,文章中将32位的float和64位的double如何存储说的很清楚,下面将相关的内容贴在下面。

1、范围
  float和double的范围是由指数的位数来决定的。
  float的指数位有8位,而double的指数位有11位,分布如下:
  float:
  1bit(符号位) 8bits(指数位) 23bits(尾数位)
  double:
  1bit(符号位) 11bits(指数位) 52bits(尾数位)
  于是,float的指数范围为-127~+128,而double的指数范围为-1023~+1024,并且指数位是按补码的形式来划分的。
  其中负指数决定了浮点数所能表达的绝对值最小的非零数;而正指数决定了浮点数所能表达的绝对值最大的数,也即决定了浮点数的取值范围。
  float的范围为-2^128 ~ +2^128,也即-3.40E+38 ~ +3.40E+38;double的范围为-2^1024 ~ +2^1024,也即-1.79E+308 ~ +1.79E+308。
2、精度
  float和double的精度是由尾数的位数来决定的。浮点数在内存中是按科学计数法来存储的,其整数部分始终是一个隐含着的“1”,由于它是不变的,故不能对精度造成影响。
  float:2^23 = 8388608,一共七位,这意味着最多能有7位有效数字,但绝对能保证的为6位,也即float的精度为6~7位有效数字;
  double:2^52 = 4503599627370496,一共16位,同理,double的精度为15~16位。

ok,说明白了吧。

参考:

http://www.cnblogs.com/BradMiller/archive/2010/11/25/1887945.html

你可能感兴趣的:(C/C++,float,double,精度)