#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的存储方式如下图所示:
![](https://img-my.csdn.net/uploads/201207/27/1343381890_1063.png)
而双精度的存储方式为:
![](https://img-my.csdn.net/uploads/201207/27/1343381932_1577.png)
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的存储方式如下图所示:
![](https://img-my.csdn.net/uploads/201207/27/1343383221_2014.png)
而单精度浮点数120.5的存储方式如下图所示:
![](https://img-my.csdn.net/uploads/201207/27/1343383249_8214.png)
那么如果给出内存中一段数据,并且告诉你是单精度存储的话,你如何知道该数据的十进制数值呢?其实就是对上面的反推过程,比如给出如下内存数据:0100001011101101000000000000,首先我们现将该数据分段,0 10000 0101 110 1101 0000 0000 0000 0000,在内存中的存储就为下图所示:
![](https://img-my.csdn.net/uploads/201207/27/1343383438_9347.png)
根据我们的计算方式,可以计算出,这样一组数据表示为:1.1101101* =120.5
而双精度浮点数的存储和单精度的存储大同小异,不同的是指数部分和尾数部分的位数。所以这里不再详细的介绍双精度的存储方式了,只将120.5的最后存储方式图给出,大家可以仔细想想为何是这样子的
![](https://img-my.csdn.net/uploads/201207/27/1343383462_5158.png)
下面我就这个基础知识点来解决一个我们的一个疑惑,请看下面一段程序,注意观察输出结果
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存储为:
![](https://img-my.csdn.net/uploads/201207/27/1343383490_3758.png)
但是这样存储方式,换算成十进制的值,却不会是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位)处理。
![](https://img-my.csdn.net/uploads/201207/27/1343383717_8715.png)
完~~~