深析C语言浮点型数据的输入输出
2019-09-09方星星吕永强
方星星 吕永强
摘 要 C语言的基本数据类型分为:整型、字符型和浮点型,大多C语言教材都概括了整型和字符型数据的编码及输入输出,但并未详细介绍浮点型数据的编码及输入输出,这导致很多学生不能灵活运用这一知识点。本文为了弥补教材的不足和便于学生更好掌握浮点数的输入输出,首先分析了float和double数据的编码,再归纳出浮点型数据按十进制、二进制和十六进制输出的三种方法,最后结合内存结构和具体事例对float数据double数据的输入作了深入分析,并阐述了double数据%f和%lf输入的区别。
关键词 输入输出;%f和%lf;浮点型数据;C语言
中图分类号:G642 文献标识码:B
文章编号:1671-489X(2019)04-0044-03
Abstract The basic data types of C language are divided into integer, character type and floating-point. Most of the C language textbooks summarize the encoding and the input and output of integer and character data, but do not introduce the encoding and the input and output of floating-point data in detail, which leads to the fact that many students cannot use this knowledge flexibly. In order to make up for the deficiency of the textbook and make it easier for students to better master the input and output of floating point numbers, this
paper first analyzes the encoding of float and double data, then summarizes three methods of floating-point data output by decimal, binary and hexadecimal system, and finally makes an in-depth analy-
sis of the input of double data of float data combined with memory structure and specific cases, and explains the difference between the input of double data by %f and by %lf.
Key words input and output; by %f and by %lf; floating-point data; C language
1 前言
浮点数即实数,分为单精度(float或single)、双精度(double)和长双精度(long double)三类。精度越高,则该类型表示的数据取值范围越大,占内存字节数越多。在C语言中,一般只涉及float类型和double类型数据,教材对于两者的输入输出都有所介绍,但对于浮点数的编码原理、十进制以外的输出形式、数据输入的原理等方面的内容提及较少。除此以外,不少教材在描述浮点型数据输入输出时,虽然指出double型数据的输入只能用%f,输出可以用%f,也可以用%lf,但并未指明原因。以上诸多因素致使学生在学习时有很多困惑。为答疑释惑,本文从浮点数编码、浮点数的三种输出形式、float数据输入和double数据输入等四个方面来阐述浮点数的输入输出。
2 float和double数据的编码
浮点数的二进制编码不同于整型数和字符型数。1985年,为了统一浮点数的存储格式,IEEE制定了IEEE 754标准。目前,绝大多数计算机都遵守这一标准,极大地改善了各种软件的可移植性。ANSI C采用该标准对float和double进行编码。
float数据的编码 根据IEEE 754的规定,浮点数在编码时首先要进行规格化,即用如下形式表示:
规格化数=符号位·尾数×2n其中,符号位为0或1,分别表示正数或负数;尾数是形式为1.XXX…XXX的二进制数,小数点之前为1(但0属于特殊情况,需要特殊处理);n为指数,二进制形式(习惯上写成十进制)。
float数据存储时占四个字节(32位),IEEE 754规定各位的意义及格式如图1所示。
在存储时,尾数中的“1.”不存储,目的是节省存储空间。另外,阶码等于规格化中的指数加上127,即阶码=指数+127。因为指数可以是负数,取值范围是-126~127(-127预留为特殊使用),为了便于处理负指数的情况,IEEE 754标准要求指数加上127后存储。如float数据1.23经过规格化后为1.0011101 01110000 10100100×20(尾数考虑二进制进位),存储时的指数为0+127=127=01111111,因此,float數据1.23的最终的存储格式如图2所示。
double数据的编码 双精度浮点数的规格化过程同float数,但在存储格式上,双精度浮点数占八个字节(64位),IEEE 754规定各位的意义及格式如图3所示。其中符号位、阶码和尾数分别占1、11和52位。另外,阶码=规格化中的指数+1023。
如double数据1.23经过规格化后为1.00111010111000010100 01111010 11100001 01000111 10101110×20(尾数考虑进位),存储时的指数为0+1023=1023=01111111111,因此,double数据1.23的最终存储格式如图4所示。
与单精度相比,双精度浮点数的阶码和尾数的位数更长,因此,双精度所能表示的数值范围更大且精度更高,可提供更多的有效数字位数,float变量只能接收十进制有效位数为7位,而double变量能接收16位有效数字。
3 浮点数的输出
浮点数有十进制、二进制和十六进制三种输出形式,但是大多数的教材只介绍了十进制形式的输出。需要说明的是,十进制形式分为小数形式和指数形式,本文只介绍小数形式输出。另外,为便于深入分析用%f和%lf输入浮点数时的真实值,在此延伸介绍浮点数的二进制和十六进制输出。
十进制输出(小数形式) 格式说明:%f是将浮点数以小数形式输出。float型数据用%f输出,doule型数据一般用%lf输出。由于printf是一个可变长度列表的函数,当调用printf函数时,float会自动转换成double类型,其结果是printf函数无法区分float类型和double类型参数。因此,printf函数中的%f既可以用于float数据输出,也可以用于double数据输出。如果变量类型为float,则只输出四个字节对应的浮点数;如果变量类型为double,会输出八个字节对应的浮点数。需要说明的是,double变量最多能接收16位有效数字,%f默认只输出小数点后6位,如果要输出更多有效位数,要加修饰符.n,即%.nf(.n表示输出小数的位数)。如%.16f用于輸出小数点后16位。
十六进制输出 格式说明:%x一般用于整型数据的十六进制输出,如果用于浮点数,则是将浮点数的二进制编码以十六进制输出。float数的十六进制输出用%x,double数的十六进制输出用%llx。如float数1.23和double数1.23按十六进制输出的结果分别为3f9d70a4和3ff3ae147ae147ae(读者可自行验证)。
二进制输出 二进制输出没有对应的格式说明,需要编写程序将变量的每个字节按二进制位进行输出。设计程序时,引入一个共用体,包含字符数组和浮点型变量两个成员,它们共用四个字节或八个字节的存储空间,定义形式如下:
union bianma{char ch[4];float a;} //处理double时成员定义为char ch[8];和double a;将字符数组元素按从后到前的顺序进行排列,每个元素都以二进制输出,最终的输出结果即为该浮点数的二进制输出。字符数据以二进制形式输出,需要进行二进制位的移位运算,对应的程序代码如下:
4 float数据的输入
文中的浮点型数据输入仅指十进制小数形式的输入。float数据的输入用%f,说明输入的是十进制小数。格式说明:%f指明输入的数据按float型处理,系统将此数据按float类型编码后存入变量占用的四个存储单元中。存储时,低地址存储单元存放数据的低字节,高地址存储单元用于存放数据的高字节。如执行语句段float a;scanf(“%f”,&a);时,键盘输入1.23,则变量a占用的四个内存单元值如图7所示(假设变量a的地址为2000H)。
5 double数据的输入
正确实现double数据的输入(只考虑十进制小数形式)只能用%lf,如果改用%f,则输入不当。
用%lf输入double数 此用法能实现正确输入,过程同float数据的输入。格式说明:%lf指明输入的数据按double处理,系统先将此数据按double类型编码,再存入变量占用的八个存储单元中。如执行语句段double b;scanf(“%f”,&b);时,输入1.23,则变量b占用的八个内存单元值如图8所示(假设变量b的地址为2000H)。
用%f输入double数 该用法不能正确输入double数。如执行语句double e;scanf(“%f”,&e);时,输入1.23,变量e的值为0。这是由于%f指明输入的数据只按float类型处理,数据按float类型编码后只存放到e占用的前四个存储单元中,即低地址存储单元。而变量e实际占用八个存储单元,系统自动将0存储到四个高地址存储单元,因此,变量e占用的八个内存单元真实值如图9所示(假设变量e的地址为2000H)。不难得出,这八个字节对应的double数十进制值为0(有效位数为15位),因此,执行语句printf(“%lf”,e);后,得出e的值为0。
6 结语
为便于掌握%f和%lf的用法,本文在分析float和double数据的编码原理基础上,提出浮点数的三种输出方法,再分别介绍float和double数据的输入,最后总结double数据用%f和%lf进行数据输入的区别。由于浮点数的编码较为复杂,C语言又严格规定了%f和%lf的用途,因此,浮点数的输入输出不同于整型和字符型,不便于掌握。在实际应用中,为避免数据输入输出结果不一致,建议float数据输入输出统一用%f,double数据输入输出统一用%lf。
参考文献
[1]谭浩强.C程序设计[M].4版.北京:清华大学出版社,2010.
[2]张亚玲.大学计算机基础:计算思维初步[M].北京:清华大学出版社,2014.
[3]朱亚超.基于IEEE 754的浮点数存储格式分析研究[J].计算机与信息技术,2006(9):50-52.
[4]田祎,樊景博.C语言中浮点数的表示范围浅析[J].软件工程,2016(4):8-10.
[5]IEEE Standard for Binary Floating-Point Arithme-tic.ANSI/IEEE Standard 754-1985[M]//Institute of Electrical and Electronics Engineers.1985.