浮点类型有效位数计算与应用分析_第1页
浮点类型有效位数计算与应用分析_第2页
浮点类型有效位数计算与应用分析_第3页
浮点类型有效位数计算与应用分析_第4页
浮点类型有效位数计算与应用分析_第5页
免费预览已结束,剩余21页可下载查看

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、    浮点类型有效位数计算与应用分析    肖红德摘 要:为弄清浮点类型数据在应用中存在“异常”现象的原因,研究了浮点类型数据在内存中的存储形式,得到与其在内存中存储规范一致的结果。分析浮点类型数据在内存中存储形式对应的理论区间并进行实验验证,得出浮点类型数据的精度(对于规范化浮点类型数据,float类型有效位数为68位,double类型有效位数为1517位),进而对浮点类型数据在应用中的一些异常现象进行合理解释,比如“大数吃小数”、输出格式控制以及输出结果与预期不一致等。通过对浮点类型数据在计算机内存中表达形式的理论分析和实验验证,实现对实际数据进行

2、离散化处理,为计算机内存中的表示和相关计算带来帮助。关键词:浮点类型;有效位数;存储单元;数制转换;不精确表示doi:10. 11907/rjdk. 182282:tp301:a:1672-7800(2019)004-0050-070 引言数据在计算机内存中是以二进制存放的1-3。基本整型int类型在存储单元中的存储方式用整数补码存放1。visual c+6.0为每一个int型数据分配4个字节(32位)。对于整数补码的求法有以下规定:一个正数的补码是其二进制形式;一个负数的补码,应先获得其绝对值的二进制形式,然后对其后所有二进位按位取反,再加12。十进制转换r进制按照整数部分除r取余和小数部分

3、乘r取整的方法进行4,5。在存放int类型数据的存储单元中,最左面一位用来表示符号,如果该位为0,则表示数值为正;如果该位为1,则表示数值为负。浮点类型数据研究主要围绕以下问题进行:浮点类型在内存中的表示形式规定;浮点类型数据表示范围;浮点类型数据精度;验证浮点类型数据和具体计算结果是否一致。对于问题,浮点类型数据在内存中的存储形式有相关规定1,3,6-19,其存储形式与整型数据在内存中的存储形式不同。浮点类型分为单精度浮点类型(float类型)和双精度浮点类型(double类型)两种。visual c+6.0编译软件为float类型数据分配4个字节,为double类型数据分配8个字节,数值以

4、规范化二进制指数形式存放在存储单元中。在存储时,系统将浮点类型数据分成小数部分和指数部分,分别存放。不论是float类型数据还是double类型数据,存储方式上都遵从ieee规范,float类型数据遵从ieee r32.24,而double类型数据遵从ieeer64.538。对于问题,文献10进行了较为详细的研究和分析。对于问题,文献9,20进行了相关浮点类型数据位数的理论计算和分析。对于问题,文献3,6通过实验验证了浮点类型数据存储格式在计算机内存中的表示形式。在下文描述中,s表示符号位,e表示指数位,e表示指数部分的位数,m表示尾数部分,b表示指数部分的偏移量(其值为b=2e-1-1)。i

5、eee标准规定,无论是float类型数据还是double类型数据在存储中都分为3个部分:符号位s:float类型和double类型都占1位,0代表正,1代表负;指数位e:float类型占8位,double类型占11位,用于存储指数数据,并且采用移位存储,指数采用移码表示(原来的实际指数值加上一个固定值),该固定值为b=2e-1-1(表示偏移量,e为指数部分比特长度,对于float类型,e=8,偏移量b=127;对于double类型,e=11,b=1 023)。指数位存储的是一个无符号整数,所以对于float类型,指数位e的取值范围为0255,其真正取值范围为-127128,对于double类型

6、,指数位e的取值范围为02 047,其真正取值范围为-1 0231 024;尾数部分m:float类型占23位,double类型占52位。尾数采用原码表示,用二进制形式,整数部分为1,那么小数点前的1就没有必要用一个比特位去存储,默认已经存在,称为“隐藏位”。所以规定尾数部分在存储时舍去第一个1,只存储小数点之后的数字,好处是对于float类型,只保存23位小数信息,加上舍去的1,可以用来表示24个有效信息;对于double类型,可以用52位尾数部分表示53个有效信息。浮点类型在内存中由高地址到低地址分别存储符号位s、指数位e和尾数部分m。指数位e还分为3种情况:指数位e不全为0,不全为1。这

7、是一种规范化的浮点数形式,此时就用正常的计算规则,指数部分e的真实值就是其字面值减去偏移量,尾数部分m的值要加上最前面省去的整数1;指数位e全为0。需要分两种情况进行处理,若尾数部分m全为0,则表示浮点数0,若尾数部分m不全为0,则表示非规范化浮点数形式,表示很小的浮点数,并且指数实际值为-(b-1)。对于float类型,指数实际值为-126。对于double類型,指数实际值为-1 022,尾数部分表示实际小数部分,整数部分为0(即没有“隐藏位”1);指数位e全为1。当尾数部分m全为0时,表示±无穷大(取决于符号位),当m不全为0时,表示该数不是一个数(nan)。本文主要研究c语言中

8、浮点类型(包括单精度浮点类型float和双精度浮点类型double)有效位数的计算、某个具体数值在内存中的存储形式、其所能表示的数据范围以及在使用过程中的“反常”现象。在程序验证过程中,采用编译软件visual c+6.0。1 数值存储形式查看数值在内存中的存储形式,已有相关验证程序可以进行查看3,6,9,11,12。在intel cpu 架构系统中,数据在内存中的存放方式为小端模式(低字节存在低地址中,高字节存在高地址中)3,21。本文设计一个专门用来显示某个变量在内存中存储形式以及按照指定形式进行输出的函数。具体实现过程为:先建立头文件“show.h”,然后在头文件中定义实现查看内存的函数

9、displaymemory。void displaymemory(void * a,int m,int n)unsigned char *b=a,c,k8=1,2,4,8,16,32,64,128;?int i,j;?for(i=m-1;i>=0;i-)? ?c=*(b+i);? ?for(j=7;j>=0;j-)? ? ? ?printf("%2d",c/kj);? ? ?c=c%kj;? ? ?printf("n");?switch(n)case 0:printf("%dn",*(int *)a);break;case

10、 1:printf("%.20en",*(float *)a);break;case 2:printf("%.20en",*(double *)a);break;default:printf("data error!n");其中,函数displaymemory 中3个形参含义如下:void *a表示数据在内存中存储的起始地址;int m表示需要查看的内存地址字节数;int n表示用何种格式符输出以地址a开始的数据,如果n为0表示以d格式符输出整数,如果n为1表示以.20e格式符、指数形式输出带20位小数部分的float类型数据,如果n

11、为2表示以.20e格式符、指数形式输出带20位小数部分的double类型数据。2 浮点数范围与有效位数2.1 浮点类型范围田祎、樊景博10对浮点类型数据范围进行了研究。本文主要按照规范化浮点数和非规范化浮点数各自取值范围分别进行讨论。规范化浮点数能够表示的数其绝对值最大值为:对于float类型,指数位e为11111110,其表示的十进制数为254-127=127,尾数部分m为全1,其对应数据为1.m*2127,所对应的十进制数约为3.402 82*1038;对于double类型,指数位为11111111110,其表示的十进制数为2 046-1 023=1 023,尾数部分m为全1,其对应数据为

12、1.m*21 023,其所对应的十进制数约为1.797 693 134 862 315 7*10308。规范化浮点数能够表示的绝对值最小非零数为:对于float类型,指数位e为00000001,表示的十进制数为1-127=-126,尾数部分m为全0,对应数据为1*2-126,其所对应的十进制数约为1.175 494 350 822 287 5*10-38;对于double类型,指数位为00000000001,表示的十进制数为1-1 023=      -1 022,尾数部分为全0,对应数据为1*2-1022,其所对应的十进制数约为2.225 0

13、73 858 507 201 2*10-308。浮点类型数据0.0在内存中的表示形式为:指数位e和尾数部分m全为0。非规范化浮点数指数位e为全0,尾数部分m不全为0。非规范化浮点数能够表示的数其绝对值最大值为:对于float类型,指数位e为全0,尾数部分m为全1,此时没有“隐藏位”1,指数部分的值为-126,其对应数据为0.m*2-126,十进制数约为1.175 494*10-38;对于double类型,指数位e为全0,尾数部分m为全1,此时没有“隐藏位”1,指数部分的值为-1 022,其对应数据为0.m*2-1 022,十进制数约为2.225 073 858 507 200 9*10-308

14、。对于非规范化浮点数,其能够表示的非零数的绝对值最小值为:对于float类型,指数位e为全0,尾数部分m最后一位为1,其余全为0,此时没有“隐藏位”1,指数部分的值为-126,其对应数据为1*2-23*2-126,十进制数约为1.401 298 464 324 817 1*10-45;对于double类型,指数位e为全0,尾数部分m最后一位为1,其余全为0,此时没有“隐藏位”1,指数部分值为-1 022,其对应数据为1*2-52*2-1 022,十进制数约为4.940 656 458 412 465 4*10-324。非规范化浮点数所表示的数都很小,并且其有效数字的位数可能为0。由浮点类型数据

15、有关形式可知,当指数位e全为1、尾数部分m全为0时,表示无穷大;当指数位e为全1、尾数部分m不全为0时,表示不是一个数(nan)。通过验证可知,对于float类型的数据,当数据范围为3.40282357e38,1.797693134862315e308時,表示无穷大,验证程序1如下:#include#include"show.h"void main()float b1=3.40282356e38,b2=3.40282357e38,b3=1.797693134862315e308;displaymemory(&b1,4,2);displaymemory(&b2

16、,4,2);displaymemory(&b3,4,2);由验证结果可知,b1为最大的规范化float类型数据,b2和b3在内存中的表示形式相同,即指数位e为全1,尾数部分m为全0。对于double类型的数据,如果超过规范化双精度浮点类型限制,编译时会提示“constant too big”错误,其无穷大能够表示的数值范围无法验证。由验证程序1可得出以下结论:float类型在计算过程中都是按照double类型进行处理的,如果表示的数超过了规范化float类型数据范围并且在double类型数据范围内,则按照float类型中的无穷大进行处理;如果超过了double类型限制,则visual

17、c+6.0编译系统认为输入的数据太大,不能进行处理。2.2 浮点类型有效位数张宗杰、张明亮9从相对误差角度对浮点数的有效位数进行了解读,逯鸿友20对不同进制之间转换位数的确定给出了理论推导。本文从尾数的取值范围和相对误差两方面进行分析,对该问题进行解释和说明。浮点数在内存中的存放是离散的,而不是连续的,即对于每一个浮点数来说,其在内存中表示的一个浮点类型数据都对应一个区间。因此,浮点类型数据是有限个。由浮点数的存储规范可知:对于非负float类型数据来说,其个数为255*223;对于非负double类型数据来说,其个数为2 047*2528。对于规范化浮点类型,按照其能够表示的二进制位数,可以

18、计算出其最大相对误差:对于float类型,当尾数部分m为0全时,取最大相对误差值约为0.5*2-23=2-24=5.960 5e-08,当尾数部分m为全1时,取最小相对误差值约为0.5*0.5*2-23=2.980 2e-08;对于double类型,当尾数部分为全0时,取最大相对误差值为0.5*2-52=2-53,约为1.110 2e-16,当尾数部分m为全1时,取最小相对误差值为0.5*0.5*2-52,约为5.551 1e-178。由相对误差的计算结果可知,对于float类型,其有效数字位数最多为8,对于double类型,其有效数字位数最多为17。而对于非规范化浮点类型,其有效数字的位数是

19、不同的,对于float类型,有效数字的位数为08,对于double类型,有效数字的位数为017,总体来说,有效数字的位数随着尾数部分有效二进制位增加而增加。对有效数字位数的确定,主要与相对误差的大小有关。由相对误差计算结果可知,对于float类型,相对误差主要影响从最高位开始的数字的第8位和第9位数字,前面7位数字在进行四舍五入进位前都是准确的,有效数字的位数需要分以下3种情况进行判断:如果内存中表示为同一个float类型数据的取值范围在第8位数字处相同,并且第9位数字取值范围进行四舍五入后与第8位数字相同,则该float类型数据有8位有效数字;如果内存中表示为同一个float类型数据的取值范

20、围在第8位数字处不相同,并且在同一个区域(04或者59),则该float类型数据有7位有效数字;如果内存中表示为同一个float类型数据的取值范围在第8位數字处不相同,并且其取值范围跨越两个不同区域(04或者59),则第8位数字取值范围影响到第7位数字的变化,并且变化后的值不同,则该float类型数据有6位有效数字。从以上判断过程可知,对于float类型,其有效数字的位数为68位。对于double类型,通过类似分析可知,其有效数字的位数为1517位。2.3 规范化浮点类型数据表示区间2.3.1 理论分析有关规范化浮点类型数据表示区间的问题,在文献1,7-11中有相关说明,主要围绕浮点数不能精确

21、表示一个数从离散角度进行了解释。本文主要从浮点类型存储形式对应的理论数据范围着手进行研究。使用fmax表示float类型数据在内存中存储形式表示数据的最大偏移程度,使用dmax表示double类型在内存中存储形式表示数据的最大偏移程度(对于给定的指数位e,无论是float类型还是double类型,其最大偏移程度表示为尾数部分m最后一位尾数为1时所表示数据的一半)。因此,fmax=0.5*2e-b-23=2e-b-24,dmax=0.5*2e-b-52=2e-b-53。浮点类型数据在内存中存储形式的确定过程如下:首先需要得到对应的二进制形式,即将十进制浮点数转换为24位二进制形式,然后再进行存储

22、。米保全4介绍了有关十进制转换为二进制的技巧。对于整数部分,十进制整数转换为二进制的规则为:除2求余,商为下次的被除数,先得到的余数为二进制的低位部分,后得到的余数为二进制的高位部分,直到商为0为止。对于小数部分,十进制整数转换为二进制的规则为:乘2取整,小数部分为下次的被乘数,先得到的整数为二进制的高位部分,后得到的整数为二进制的低位部分,直到小数部分为0或者加上前面整数部分转换的二进制位超过了指定位数的二进制为止(float类型为24位,double类型为53位)。最后计算结果,对于规范化float类型数据保留前面最高24位二进制数字,对于规范化double类型数据保留前面最高的53位二进

23、制数字。多于指定位数的二进制部分需要进行向上进位或者舍弃处理(通过验证程序2可知,按照就近原则靠拢,如果距离两个数值相同,则采用进位和舍弃交替进行的方式处理)。如果将非负浮点数在内存中的形式从1开始按照自然数形式进行编号,1对应内存中存储形式为0的数据,2对应最小正浮点数,则对于float类型,当指数位取值小于255时,编号i取值范围为1,255*223。通过验证程序2可知,编号i所能表示数的范围可以用开、闭区间进行表示:当i为奇数时,相应内存数据对应的实际范围为闭区间,否则为开区间。对于float类型,编号i与对应指数位e以及尾数部分m之间关系为:由于指数部分确定时,其尾数部分出现的可能情况

24、为223,是偶数,开闭区间交替出现,所以编号i的开闭情况与指数位e无关,只与尾数部分m的情况有关。如果把尾数部分m看作23位二进制整数,那么当尾数部分m为偶数时是闭区间,当尾数部分m为奇数时是开区间,即当23位尾数部分的最后一位为0时是闭区间,当其最后一位为1时是开区间。doulbe类型数据分析过程与此类似,不再赘述。2.3.2 区间确定由上述尾数与开闭区间之间关系以及内存中存储形式表示数的最大偏移程度,对于内存中的任意存储形式,其所对应数据范围也就确定下来了。目前的问题是如何确定最大偏移程度。对于浮点类型,当指数位确定时,其最大偏移程度是确定的。对于float类型,fmax=2e-b-24;

25、对于double类型,dmax=2e-b-53。当尾数部分不全为0时,内存中存储数据对应的浮点数t前后最大偏移程度是相同的。当最后一位尾数部分为0时,表示的数据范围为闭区间,即对于float类型为t-fmax,t+fmax,对于double类型为t-dmax,t+dmax;当最后一位尾数部分为1時,表示的数据范围为开区间。当尾数部分为全0时,其最大偏移程度与两个相邻指数位部分有关。对于相邻两个指数位e'和e=e'+1:对于float类型,fmax=2e-b-24,数据前面的最大偏移量为0.5*fmax,数据后面的最大偏移量为fmax,即对于float类型数据t,其所能表示的区间

26、为t-0.5*fmax,t+fmax;对于double类型,dmax=2e-b-53,其所能表示的区间为t-0.5*dmax,t+dmax,即相邻两个指数部分的最大偏移量相差1倍。浮点数不能精确地表示一个数字,规范化float类型数据有68位有效数字,double类型数据有1517位有效数字,在计算一个内存中存储形式对应的浮点数表示区间时,一般情况下会与理论值有微小误差。而且对于浮点数,vc+6.0在计算过程中都是按照double类型进行计算1,而double类型所能表示的有效数字位数为1517位,因此在表示float类型数据对应的理论区间时需要按照double类型的计算过程进行,即对于flo

27、at类型数据的表示区间a,b或(a,b),对于a的表示,需要按照double类型数据对待,即只要区间c,d或(c,d)内的值表示double类型a的值即可,类似可以得到对于b的取值范围为e,f或(e,f)。从而可以得到对应float类型数据的表示区间需要使用区间c,f (d,e)进行表示,即对于float类型,当尾数部分m不全为0、最后一位尾数部分为0时,float类型数据t的理论区间为 t-fmax-dmax,t+fmax+dmax,当最后一位尾数部分不为0时为开区间,表示为(t-fmax+dmax,t+fmax-dmax),其中fmax=2e-b-24为float类型下最大偏移量,dmax

28、=2e-b-53为对应double类型下最大偏移量;当尾数部分m为全0时,对于相邻两个指数位e'和e=e'+1,则对于指数部分为e的第一个数值(即尾数部分m为全0),float类型数据t的理论区间为t-0.5*fmax-0.5*dmax,t+fmax+dmax,其中fmax=2e-b-24为float类型下最大偏移量,dmax=2e-b-53为对应double类型下最大偏移量。因此,对于内存中存储形式对应的float类型数据t,可以得出以下结论:(1)当尾数部分m全为0时,内存中存储形式对应数据t表示的数据区间为:(2)当尾数部分m不全为0并且最后一位为0时,内存中存储形式对应

29、数据t表示的数据区间为:(3)当尾数部分m不全为0并且最后一位为1时,内存中存储形式对应数据t表示的数据区间为:其中,fmax=2e-b-24为float类型下的最大偏移量,dmax=2e-b-53为对应double类型下的最大偏移量。2.3.3 区间验证对于尾数部分全为0的float类型数据,比如float类型数据167 772 16在内存中进行存储时,通过计算可知,其对应的二进制表示为1 00000000 00000000 00000000,即224,在内存中的存储形式为01001011 10000000 00000000 00000000。167 772 15.5距离167 772 15

30、与167 772 16相同,对167 772 16进行向上进位处理。由式(1)可知,其对应的理论区间为167 772 16-0.5*fmax-0.5*dmax,167 772 16+fmax+dmax,其中fmax=2151-127-24=1,dmax=21 047-1 023-53=1.862 6e-09,即理论区间值为167 772 16-0.5*1-0.5*1.862 6e-09, 167 772 16+1.0+1.862 6e-09。验证程序2如下:#include#include"show.h"void main()float b1=16777216-0.5-9.

31、3133e-10,b2=16777216-0.5-9.3132e-10,b3=16777216+1.0+1.8626e-09,b4=16777216+1.0+1.8627e-09;displaymemory(&b1,4,1);displaymemory(&b2,4,1);displaymemory(&b3,4,1);displaymemory(&b4,4,1);从验证结果可知,b2和b3输出的是float类型数据    167 772 16,而b1和b4输出的不是该数据,从而验证了上文理论结果是成立的。类似可以验证167 772 1

32、9距离    167 772 18和167 772 20相同,对167 772 20进行向上进位处理,167 772 21距离167 772 20和167 772 22相同,对   167 772 20进行舍弃处理等。对于尾数部分不全为0的float类型,比如,对于float类型数据838 860 9,通过计算可知其在内存中的存储形式为01001011 00000000 00000000 00000001,符号位s为0,表示正数,指数位e存放的数对应整数值为150,尾数部分m最后一位为1,其余全为0。则其在内存中的存储值为:1*2150-127

33、+1*2-23*2150-127=838 860 9。由式(3)可知,其能够表示的数据范围通过计算可知为(838 860 9-fmax+dmax,838 860 9+fmax-dmax),其中fmax=2150-127-23-1=0.5,dmax=21 046-1023-52-1=9.313 2e-10,即(838 860 9-0.5-9.313 2e- 10,838 860 9+0.5+9.313 2e-10)。与验证程序2类似,可以验证该区间。2.4 非规范化浮点类型数据有效数字位数非规范化浮点类型有效数字位数与尾数部分高位处开始出现的1的位置有关,从1开始到尾数部分结束的位数决定了对应十

34、进制有效数字位数。总体来说,从尾数部分高位处开始出现1的位置越靠前,有效数字的位数就越多,非规范化浮点类型表示非常小的数字。比如,对于float类型数据,其在内存中的存储形式为:00000000 00000000 00000000 00000001,即符号位s为0表示正,指数位e为全0表示真实指数值为-126,尾数部分m只有最后一位为1,其余全为0,该存储形式通过计算可知其理论值为: 1*2-23*2-126=2-149,而對该数值的十进制形式计算比较麻烦,因此,在下文验证过程中,采用对指定float类型数据存储位置赋值的方式对该数据赋值,然后输出该最小正float类型数据的指数形式,从而确定

35、最小正float类型在内存中的表示形式及其所对应的十进制指数形式。验证程序3如下:#include?typedef?struct?fp_single?unsigned?_int32?m:23;?unsigned?_int32?e:8;?unsigned?_int32?s:1;?fp_single;void?main()float?x;fp_single?*?fp_s=&x;fp_s->s=0;fp_s->e=0;fp_s->m=1;?printf("float最小正非规范数:%le n",x);从运行结果可知,最小正float类型所能表示的非规范化

36、数据为1.4012984643248171e-045。由式(3)可知,该运行结果表示的是一个范围,其最大偏移程度为该数的一半。与验证程序2类似,将b1、b2、b3、b4分别初始化为:7.00649232162408613e-46、7.00649232162408614e-46、2.10194769648722545e-45、2.10194769648722546e-45,可以验证该理论区间。由验证结果可知,b1和b4在内存中存储的是不同的数值,而b2和b3在内存中存储的是同一个值1.4012984643248171e-045,而b2-b3范围内的值与内存中存储的值相比,有效数字位数可能为0。由

37、上述验证结果可以得出以下结论:对于float类型数据,其非规范化数据有效数字的位数可能为0,总体来说,随着非规范化数值增大,其有效数字的位数也逐渐增多,直到增加到68位为止,即其有效数字的位数在08位之间。对于double类型数据,其非规范化数据有效数字的位数也有类似结论,即其有效数字位数在017位之间。3 浮点数使用注意问题3.1 数据丢失与不能精确比较对于浮点数在计算中的丢失现象,杜叔强等12,13给出了相关建议。本文主要从float类型在计算过程中以double类型进行计算的角度进行解释和分析。比如,float类型变量a=123 456 789 00,b=50;则a+b的结果不是123

38、456 789 50,在内存中,a和b的存储形式都是01010000 00110111 11110111 00000111,其值为     123 456 788 48,因为a和b在计算时有效数字位数多于9位,不能精确存储。该数123 456 789 00在内存中存储的值为123 456 788 48,该值加上50之后,没有超过最大偏移量,还是123 456 788 48。通过计算可知,float类型数据123 456 788 48在内存中存储的指数位e为160,fmax=2160-127-23-1=29=512,dmax=21056-1023-52-1

39、=2-20=9.5367e-07,由式(3)可知,其表示的范围为开区间(123 456 788 48-fmax+dmax,12345678848+fmax-dmax),即(123 456 788 48-512+9.5367e-07,123 456 788 48+512- 9.5367e-07),如果计算结果在该区间,则都会以             123 456 788 48进行存储和显示。与验证程序2类似的过程,可以验证该区间。由该现象可以得到一个结论:尽量不要使用两个差

40、别较大的浮点类型数据进行运算,否则会出现“大数吃小数”的现象。比较两个数的差别,以较小数是否大于较大数的最大偏移量为准,如果较小数大于较大数的最大偏移量,则认为两个数差别不大,可以直接进行运算,否则,要尽量避免直接进行运算。类似可以验证,只要一个数加上数据的绝对值小于该数在内存中的最大偏移量,无论先后加上多少个该类数据,都是该数据本身。验证程序4如下:#include#include"show.h"void main()float b=12345678848,b1;int  i;displaymemory(&b,4,1);b1=b;for(i=0;idis

41、playmemory(&b1,4,1);从计算结果来看,b和b1输出结果相同,因此要尽量避免进行类似运算,即尽量避免将一个较大数和一个较小数(较小数小于较大数在内存中对应存储形式的最大偏移量)进行运算。如果想让较小数起作用,则需要多个较小数在同一个表达式中出现,因为float类型数据是按照double类型数据进行计算的,double类型数据规范化形式有1517位有效数字,可以将部分较小的数据保留下来,最后再将计算得到的double类型数据自动转换为float类型数据进行保存。验证程序5如下:#include#include"show.h"void main()flo

42、at b=12345678848,b1;int  i;displaymemory(&b,4,1);b1=b+500+500;displaymemory(&b1,4,1);从计算结果来看,b和b1输出结果不同,原因是对于b1的计算将b与多个较小数直接加在一起,在运算时按照double类型进行计算,因此能够保留较多有效位数,只要多个较小数加在一起超过了较大数的最大偏移量,就可以得到改变后的数。由于浮点数表示的数据不精确,一定范围内数值在内存中存储的形式相同,因此应该尽量避免两个接近的浮点数进行相等和不相等比较。比如两个float类型的变量a=12345678900、b=1

43、2345678950,在进行a=b的条件判断时结果为真,因为a和b在内存中的存储形式相同,认为这两个变量相等。验证程序6如下:#include#include"show.h"void main()float a=12345678900,b=12345678950;if(a=b)printf("a=bn");else printf("a!=bn");从运行结果来看,会输出“a=b”,因为对于浮点类型变量a和b,其在内存中的存储形式相同,因此条件“a=b”是成立的。所以,对于浮点类型变量,应避免进行相等和不相等的判断。3.2 不同类型数据

44、输出时出现反常现象c语言中常用的数据输出格式符有d、c、f1,对于不同数据类型需要使用不同格式符进行输出,如果不小心用了不该用的格式符,则输出结果会与预期结果不同。字符型数据可以按照c格式符或者d格式符进行输出,分别按照字符形式或者对应的十进制整数形式进行输出;基本整型数据可以按照d格式符输出其十进制整数形式进行输出;f格式符用来输出浮点类型数据的十进制小数形式,如果要输出指数形式,则以e格式符进行输出,f格式符和e格式符都默认输出6位小数部分。如果数据输出时没有按照其正常格式符进行输出,则输出结果按照对应格式符存储形式要求进行输出。比如:double a=2.5,printf(“%dn”,*

45、(int *) &a),输出结果是0,因为2.5在内存中按照double类型进行存储,占8个字节,其在内存中的存储形式为01000000 00000100 00000000 00000000 00000000 00000000 00000000 00000000,而输出时按照后4个字节整数对应的存储形式规则进行读取。验证程序7如下:#include#include"show.h"void main()double a=2.5;displaymemory(&a,8,2);printf("%dn",a);如果把整数按照f格式符进行输出,将整数

46、的地址按照浮点类型地址格式进行读取,则读取过程中会按照浮点类型的规则进行处理。比如:int a=655 36,printf(“%en”,a),变量a在内存中的存储形式为00000000 00000001 00000000 00000000。单精度浮点类型数据的输出结果是9.1835496157991212e-041,而双精度浮点类型(因为f或e格式符默认将相应参数看作双精度浮点类型数据进行输出)输出结果是5.597333e-308。由前面浮点类型在内存中存储形式的规定可知:如果按照单精度浮点类型数据的处理方式进行处理,该存储形式对应的单精度浮点数是一个非规范化浮点数;如果按照雙精度浮点类型数据

47、的处理方式进行处理,需要变量a的地址和其前面4个字节的地址作为双精度浮点类型数据在内存中的存储形式进行处理,由于变量a的地址前面4个字节存储形式不固定,因此,得到双精度浮点类型数据在不同运行环境下一般是不同的,其测试存储形式如下:00000000 00010010 11111111 11000000 00000000 00000001 00000000 00000000。验证程序8如下:#include#include"show.h"void main()int a=65536;displaymemory(&a,8,2);printf("%en",a);因此,对于不同數据类型,需要使用其对应的格式符进行输出格式控制,否则会按照对应输出格式的数据进行处理和输出。4 结语不同数据类型在计算机内存中的存放方式不同,导致一些与整型类型不一样的用法,比如数据有效位数限制、不能精确比较、“大数吃小数”等问题出现。只有了解不同数据的存储长度

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论