C语言 float和double型数据在内存中的存储方式

#include <stdio.h>
/************************************************************************/
/* 3_tanxinsuanfa             找零钱                              */
/************************************************************************/
void main( int argc , char *argv[] )
{
	float money = 0;
	int yuan100 = 0, yuan50 = 0, yuan20 = 0, yuan10 = 0, yuan5 = 0, yuan1 = 0, coin5 = 0, coin2 = 0, coin1 = 0;
	printf ( "请输入要找金额(精确到角):" );
	scanf ( "%f" , &money );

	while ( money >= 100.0f )
	{
		yuan100++;
		money = money - 100.0f;
	}
	while ( money >= 50.0f )
	{
		yuan50++;
		money = money -50.0f;
	}
	while ( money >= 20.0f )
	{
		yuan20++;
		money = money - 20.0f;
	}
	while ( money >= 10.0f )
	{
		yuan10++;
		money = money - 10.0f;
	}
	while ( money >= 5.0f )
	{
		yuan5++;
		money = money - 5.0f;
	}
	while ( money >= 1.0f )
	{
		yuan1++;
		money = money - 1.0f;
	}
	while ( money >= 0.5f )
	{
		coin5++;
		money = money - 0.5f;
	}
	while ( money >= 0.2f )
	{
		coin2++;
		money = money - 0.2f;
	}
	while ( money >= 0.1f )
	{
		coin1++;
		money = money - 0.1f;
	}

	printf ( "100元:\t%3d张\n" , yuan100 );
	printf ( "50元:\t%3d张\n" , yuan50 );
	printf ( "20元:\t%3d张\n" , yuan20 );
	printf ( "10元:\t%3d张\n" , yuan10 );
	printf ( "5元:\t%3d张\n" , yuan5 );
	printf ( "1元:\t%3d张\n" , yuan1 );
	printf ( "5角:\t%3d张\n" , coin5 );
	printf ( "2角:\t%3d张\n" , coin2 );
	printf ( "1角:\t%3d张\n" , coin1 );
	
}


模仿别人的贪心算法写了个找零钱的小程序,本来相安无事!
这是运行结果:
  
   随手将 float money = 0;的float改为了double,我想c编译器不是默认将浮点作为双精度(double)来处理的,那改为double应该也无妨。
但是,结果不对了!
  
  这是调试窗口看到的 money的值,这个值让我很不淡定了。。。
  
  我想起了---浮点型数据在内存中的存放方式
  scanf ( "%f" , &money );
  将一个64位数据的地址赋给了一个32位数据的地址,所以我猜想,应该是由于,数据在内存中存放形式不同导致结     果出错。

  于是,谷歌一下~~~找到了这两篇文章

                                                                          文章一

float和double在内存中的存储方式

     转:float和double在的存储方式 作者: jillzhang 联系方式:jillzhang@126.com

    C语言和C#语言中,对于浮点类型的数据采用单精度类型(float)和双精度类型(double)来存储,float数据占用32bit,double数据占用64bit,我们在声明一个变量float f= 2.25f的时候,是如何分配内存的呢?如果胡乱分配,那世界岂不是乱套了么,其实不论是float还是double在存储方式上都是遵从IEEE的规范的,float遵从的是IEEE R32.24 ,而double 遵从的是R64.53。
无论是单精度还是双精度在存储中都分为三个部分:
1. 符号位(Sign) : 0代表正,1代表为负
2. 指数位(Exponent):用于存储科学计数法中的指数数据,并且采用移位存储
3. 尾数部分(Mantissa):尾数部分
其中float的存储方式如下图所示:
  

  而双精度的存储方式为:
  

   R32.24和R64.53的存储方式都是用科学计数法来存储数据的,比如8.25用十进制的科学计数法表示就为:8.25* ,而120.5可以表示为:1.205* ,这些小学的知识就不用多说了吧。而我们傻蛋计算机根本不认识十进制的数据,他只认识0,1,所以在计算机存储中,首先要将上面的数更改为二进制的科学计数法表示,8.25用二进制表示可表示为1000.01,我靠,不会连这都不会转换吧?那我估计要没辙了。120.5用二进制表示为:1110110.1用二进制的科学计数法表示1000.01可以表示为1.00001* ,1110110.1可以表示为1.1101101* ,任何一个数都的科学计数法表示都为1.xxx* ,尾数部分就可以表示为xxxx,第一位都是1嘛,干嘛还要表示呀?可以将小数点前面的1省略,所以23bit的尾数部分,可以表示的精度却变成了24bit,道理就是在这里,那24bit能精确到小数点后几位呢,我们知道9的二进制表示为1001,所以4bit能精确十进制中的1位小数点,24bit就能使float能精确到小数点后6位,而对于指数部分,因为指数可正可负,8位的指数位能表示的指数范围就应该为:-127-128了,所以指数部分的存储采用移位存储,存储的数据为元数据 127,下面就看看8.25和120.5在内存中真正的存储方式。
首先看下8.25,用二进制的科学计数法表示为:1.00001*
按照上面的存储方式,符号位为:0,表示为正,指数位为:3 127=130 ,位数部分为,故8.25的存储方式如下图所示:
  

 而单精度浮点数120.5的存储方式如下图所示:
 

  那么如果给出内存中一段数据,并且告诉你是单精度存储的话,你如何知道该数据的十进制数值呢?其实就是对上面的反推过程,比如给出如下内存数据:0100001011101101000000000000,首先我们现将该数据分段,0 10000 0101 110 1101 0000 0000 0000 0000,在内存中的存储就为下图所示:
  

  根据我们的计算方式,可以计算出,这样一组数据表示为:1.1101101* =120.5
而双精度浮点数的存储和单精度的存储大同小异,不同的是指数部分和尾数部分的位数。所以这里不再详细的介绍双精度的存储方式了,只将120.5的最后存储方式图给出,大家可以仔细想想为何是这样子的
  
下面我就这个基础知识点来解决一个我们的一个疑惑,请看下面一段程序,注意观察输出结果
float f = 2.2f;
double d = (double)f;
Console.WriteLine(d.ToString("0.0000000000000"));
f = 2.25f;
d = (double)f;
Console.WriteLine(d.ToString("0.0000000000000"));

   可能输出的结果让大家疑惑不解,单精度的2.2转换为双精度后,精确到小数点后13位后变为了2.2000000476837,而单精度的2.25转换为双精度后,变为了2.2500000000000,为何2.2在转换后的数值更改了而2.25却没有更改呢?很奇怪吧?其实通过上面关于两种存储结果的介绍,我们已经大概能找到答案。首先我们看看2.25的单精度存储方式,很简单 0 1000 0001 001 0000 0000 0000 0000 0000,而2.25的双精度表示为:0 100 0000 0001 0010 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000,这样2.25在进行强制转换的时候,数值是不会变的,而我们再看看2.2呢,2.2用科学计数法表示应该为:将十进制的小数转换为二进制的小数的方法为将小数*2,取整数部分,所以0.282=0.4,所以二进制小数第一位为0.4的整数部分0,0.4×2=0.8,第二位为0,0.8*2=1.6,第三位为1,0.6×2 = 1.2,第四位为1,0.2*2=0.4,第五位为0,这样永远也不可能乘到=1.0,得到的二进制是一个无限循环的排列 00110011001100110011... ,对于单精度数据来说,尾数只能表示24bit的精度,所以2.2的float存储为:
  

但是这样存储方式,换算成十进制的值,却不会是2.2的,应为十进制在转换为二进制的时候可能会不准确,如2.2,而double类型的数据也存在同样的问题,所以在浮点数表示中会产生些许的误差,在单精度转换为双精度的时候,也会存在误差的问题,对于能够用二进制表示的十进制数据,如2.25,这个误差就会不存在,所以会出现上面比较奇怪的输出结果。

                                                                     
文章二
                                                                作者:愤怒的青蛙
         最近在做一个数据格式分析和转换的项目,第一次接触底层的二进制代码存储,看的一头雾水,看到这个帖子后对于在Windows系统下数据的存储方式有了更多的了解,将原文分享一下:
    原文地址为http://www.cppblog.com/aaxron/archive/2011/12/03/161347.html
    C语言中float,double类型,在内存中的结构(存储方式)
从存储结构和算法上来讲,double和float是一样的,不一样的地方仅仅是float是32位的,double是64位的,所以double能存储更高的精度。
任何数据在内存中都是以二进制(0或1)顺序存储的,每一个1或0被称为1位,而在x86CPU上一个字节是8位。比如一个16位(2 字节)的short int型变量的值是1000,那么它的二进制表达就是:00000011 11101000。由于Intel CPU的架构原因,它是按字节倒 序存储的,那么就因该是这样:11101000 00000011,这就是定点数1000在内存中的结构。
目前C/C++编译器标准都遵照IEEE制定的浮点数表示法来进行float,double运算。
这种结构是一种科学计数法,用符号、指数和 尾数来表示,底数定为2——即把一个浮点数表示为尾数乘以2的指数次方再添上符号。
下面是具体的规格:
类型 符号位 阶码 尾数 长度
float 1 8 23 32
double 1 11 52 64
临时数 1 15 64 80
由于通常C编译器默认浮点数是double型的,下面以double为例: 共计64位,折合8字节。
由最高到最低位分别是第63、62、61、……、0位: 最高位63位是符号位,1表示该数为负,0正; 62-52位,一共11位是指数位; 51-0位,一共52位是尾数位。
按照IEEE浮点数表示法,下面将把double型浮点数38414.4转换为十六进制代码。
把整数部和小数部分开处理:整数部直接化十六进制:960E。小数的处理: 0.4=0.5*0+0.25*1+0.125*1+0.0625*0+…… 实际上这永远算不完!这就是著名的浮点数精度问题。所以直到加上前面的整数部分算够53位就行了(隐藏位技术:最高位的1 不写入内存)。
如果你够耐心,手工算到53位那么因该是:38414.4(10)=1001011000001110.0110101010101010101010101010101010101(2)
科学记数法为:1.001……乘以2的15次方。指数为15! 于是来看阶码,一共11位,可以表示范围是-1024 ~ 1023。因为指数可以为负,为了便于计算,规定都先加上1023,在这里, 15+1023=1038。
二进制表示为:100 00001110 符号位:正—— 0 ! 合在一起(尾数二进制最高位的1不要): 01000000 11100010 11000001 11001101 01010101 01010101 01010101 01010101 按字节倒序存储的十六进制数就是: 55 55 55 55 CD C1 E2 40。

       好,再回到最开始的问题:
     scanf ( "%f" , &money );
     这一句,将一个64位数据的地址赋给了一个32位的地址。也就是说,现在你只给电脑32个bit位,但是却要苦逼的电脑用浮点数的科学计数法来表示double(64位)的数据,人家电脑老实啊,他就把 最高位做了符号位,后面的11位做了阶码,剩下的20位做了尾数。结果就^_^……

     其实,scanf ( "%f" , &money ); 包含了一个默认的参数提升的概念。就是,float都会被转换成double之后再传参,所以%f输出就是double。但是,folat的占位符是%f,而double的占位符是%lf。然而读取double变量需要在scanf()中使用%lf而不能是%f。

所以,将

scanf ( "%f" , &money ); 改为 scanf ( "%lf" , &money );并将数字后面的f后缀去掉。就可以完美运行了!

   啥~~~为嘛要加个f,人老谭说了:c语言编译系统将浮点型常量作为双精度来处理。
   例如:float f = 2.45678 * 4523.65;系统先把2.45678 和 4523.65作为双精度数,然后进行运算,得到的结果也是一个双精度数。最后取其前7位赋给浮点型变量 f 。这样做可以使计算结果更准确。但是,运算速度降低了。如果,在数字的后面加字母 f 或者 F (如 1.65f、65.487F),这样编译系统就会把他们按单精度(32位)处理。
   

这是结果~~~
  
 
  完~~~

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: C语言floatdouble是两种不同的数据,用于存储单精度和双精度浮点数。 float变量占用4个字节(32位)内存空间,精度约为7位小数。 double变量占用8个字节(64位)内存空间,精度约为15位小数。 定义方法: float num1; double num2; 赋值方法: num1 = 1.23f; num2 = 3.14159; 使用方法: printf("num1=%f,num2=%lf",num1,num2); 由于double精度高,所以在科学计算或金融计算,一般使用double。如果空间允许,更建议使用double。 ### 回答2: C语言的浮点数类有两种:floatdoublefloat可以表示小数点后7位有效数字,double可以表示小数点后15-16位有效数字。 在实际应用float常用于存储占用空间较小的小数,比如表示温度、压力、声音等物理量;double则常用于需要更高精度的计算,比如计算科学或金融领域。 C语言使用浮点数的方法和整变量十分相似,可以进行加、减、乘、除等基本运算,也可以使用赋值、条件语句、循环等常见操作。需要注意的是,在浮点数的计算可能会出现超出范围的情况,比如除以0或者计算结果过大过小。这时候应该使用判断语句,或者特定的库函数(比如math.h的函数)进行异常处理,避免计算错误。 在编写程序时,应该根据实际情况选择适合的浮点数类,并严格遵循“尽量使用小的占用空间”的原则,避免浪费内存资源。同时也应该注意浮点数的精度问题,避免舍入误差对计算结果造成影响。 总之,C语言floatdouble,是处理小数运算时十分重要的数据。合理、准确地使用这些类,可以使程序运行效率更高,计算结果更精确。 ### 回答3: C语言floatdouble是浮点数据float数据占据4个字节(32位),而double数据占据8个字节(64位)。 在C语言floatdouble主要用于存储小数或者浮点数。float数据可以存储的范围是1.2E-38~3.4E38,而double数据可以存储的范围是2.3E-308~1.7E308。因此,double数据存储范围比float更广泛。 使用floatdouble可以提高程序的精度和准确度。当程序需要不断进行小数计算时,floatdouble可以提供更加准确的计算结果。此外,在进行科学计算、金融计算、图形图像处理等领域的计算时,floatdouble也经常被使用。 在C语言,使用floatdouble定义变量时需要在数字后面加上f或d,以表明该数字是floatdouble。例如:float num1 = 1.23f; double num2 = 2.4567d; 同时,C语言提供多个数学库函数,可用于计算浮点数的数学函数,例如sqrt(开平方)、sin(正弦函数)等等,这些函数使用时要传入float或者double数据的参数。 总之,floatdouble数据C语言广泛应用于数值计算和科学计算等领域,提供了更加精确的计算结果。需要注意的是,在运算时应注意精度损失问题,避免误差的产生。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值