上一篇<一起学习C语言:C语言数据类型(二)>中,我们了解了整形和字符类型的定义,以及二进制到八进制、十进制和十六进制转换方式。本篇文章中,我们进行分析浮点数的运算,并通过二进制、十进制和十六进制表达浮点数值。
章节预览:
1.4 浮点数类型
1.4.1 浮点数的表示方法
1.4.2 单精度浮点数类型
1.4.3 关于浮点数精度问题
1.4.4 实数转换到单精度浮点数
1.4.5 单精度浮点数取值范围
1.4.6 双精度浮点数类型
1.4.7 双精度浮点数取值范围
1.5 本章总结
1.6 练习题
目录预览
章节内容:
1.4 浮点数类型
浮点数类型采用实数形式展示,它由符号位、指数位和尾数位构成,因此,不能直接按照二进制数字方式计算。另外,浮点数类型在编译器中只以有符号类型形式存在,下面我们来分析它的组成结构和运算方式。
1.4.1 浮点数的表示方法
在IEEE-754标准中,浮点数的定义如下:
把一个数的有效数字和数的范围在计算机的一个存储单元中分别予以表示,数的小数点位置随比例因子的不同而在一定范围内自由浮动。
一个R进制数N使用科学表达式表示为N = R^e * M(或M * R^e),其中R表示基数、M表示尾数、e表示指数。
1.4.2 单精度浮点数类型
float 表示单精度浮点数类型,在16位、32位以及64位系统下都占用四个字节(Byte)。参考图1.4-1 单精度浮点数结构表,我们来分析实数的计算方式:
bit(位) | 31bit | (23-30)bit | (0-22)bit |
---|---|---|---|
数据描述 | 符号域 | 指数域 | 尾数域 |
尾数域:
数据范围0—22bit,共23位(bit),用来表示实数转换为二进制数字的部分,当转换后的二进制数字不足23位时,采用低位补0对齐。下面举个例子,实数5.25:
整数部分 :直接采用二进制转换得到 101;
小数部分 :采用“乘2取整”运算,如果结果大于等于1取二进制数字1,否则取二进制数字0,然后用得到的结果小数部分继续运算,一直到小数部分为0或尾数域足够23位:
0.25 * 2 = 0.5,取0;
0.5 * 2 = 1,取1;
小数部分得到 01,整数和小数部分组合为 101.01;
二进制科学表达式 :规范要求浮点数的小数点左侧必须为1,我们采用小数点左移得到1.0101,这个示例中我们左移了两位,表示为 1.0101 * 2^2;
尾数域完整数据 :尾数数据储存时,应省略小数点前面的1,这么做是为了省出一位二进制数字,用来存储更多的数据,按左高位右低位表示为 010 1000 0000 0000 0000。
指数域:
指数域由8位介码(E)采用移码方式来表示正负指数,数据范围23—30bit,共8位(bit),介码有效范围为1—254,偏移量为127,介码(E) = 127 + 元数据⑵。示例中,指数域计算方式:
实数5.25计算尾数域时,小数点左移了两位,所以我们的元数据为2,然后通过127 + 2 得到介码129,二进制数字表示为 1000 0001。
(2) :指数(e)的最高位为符号位,其余位为数据位,可表达有效范围为-126—127,指数(e) = 介码(E) – 127。
符号域:
数据为31bit,占1位(bit),0表示正数,1表示负数。
bit(位) | 31bit | (23-30)bit | (0-22)bit |
---|---|---|---|
实数5.25 | 0 | 1000 0001 | 010 1000 0000 0000 0000 0000 |
参考图3.4-2 实数5.25组成部分,我们得到二进制数字 0100 0000 1010 1000 0000 0000 0000 0000,十六进制 0x40A8000。
1.4.3 关于浮点数精度问题
浮点数为什么用精度标记?原因很简单,浮点数尾数域有限,比如单精度浮点数,只能存储23位二进制数字,多余的部分会被抛弃,关于这个问题,我们还是用例子的形式表示,参考实数5.168:
整数部分 :101;
小数部分 :0010101100000010011110101110110011001100…
这个示例中,小数部分从1100开始无限循环,内存中只存储这个二进制数字的前23位,得到的只是这个数字的接近值,这属于由精度问题产生的其中一种情况。
在编译器中使用float 类型储存5.168时,得到实际数据为5.1680002,在实际使用时,如果必须用到更高精度的浮点数,可以使用双精度浮点数类型。
1.4.4 实数转换到单精度浮点数
当我们得到一串数字,比如十六进制C14A0000,我们可以先把这串数字转换为二进制数字然后放到相应区域进行计算:
转换为二进制数字:
bit(位) | (28-31)bit | (24-27)bit | (20-23)bit | (16-19)bit | (12-15)bit | (8-11)bit | (4-7)bit | (0-3)bit |
---|---|---|---|---|---|---|---|---|
十六进制数字 | C | 1 | 4 | A | 0 | 0 | 0 | 0 |
二进制数字 | 1100 | 0001 | 0100 | 1010 | 0000 | 0000 | 0000 | 0000 |
参考图1.4-2 实数C14A0000,转换到二进制数字为 1100 0001 0100 1010 0000 0000 0000 0000,然后数据放入相应区域:
bit(位) | 31bit | (23-30)bit | (0-22)bit |
---|---|---|---|
实数 C14A0000 | 1 | 1000 0010 | 100 1010 0000 0000 0000 |
数据描述 | 符号域 | 指数域 | 尾数域 |
尾数域 :100 1010 0000 0000 0000 0000;
指数域 :1000 0010;
符号域 :1;
首先计算指数域:
指数e = 1000 0010 – 0111 1111 = 0000 0011,转换到十进制数字为3。
然后计算尾数域:
首先把最低位为1之后的0去掉得到 100 101,二进制科学表示方式为 1.100 101 * 2^3,实数N等于:
N = (1 * 2^0 + 1 * 2^-1 + 1 * 2^-4 + 1 * 2^-6) * 2^3
N = (1 + 0.5 + 0.0625 + 0.015625) * 8
N = 12.625
最后计算符号域:
符号位为1,表示这个数为负数,十进制数字为 -12.625。
1.4.5 单精度浮点数取值范围
之前介绍浮点数存储的数字与部分实际数字有些偏差,在取值范围这部分也存在着偏差,浮点数范围主要由指数域决定的,比如单精度浮点数:正数指数值最大有效值是127,可以得到正数最大理论值1.111 1111 1111 1111 1111 1111 * 2^127,接近2 * 2^127,VS2010编译器中为3.402823466e+38F;负数指数最小有效值是-126,可以得到正数最小理论值1.111 1111 1111 1111 1111 1111 * 2^-126,接近2 * 2^-126,VS2010编译器中为1.175494351e-38F。负数最大、最小理论值与正数刚好相反,当数据接近-2 * 2^127时,为负数最小值,数据接近-2 * 2^-126时,为负数最大值。在大部分编译器中,单精度浮点数可以表示包含小数在内的6-8位十进制数字。
1.4.6 双精度浮点数类型
在部分领域中,需要使用高精度的数字,比如纳秒与秒级单位之间的转换使用单精度浮点数无法显示完整的十进制数,这种情况下,应使用双精度浮点类型存储。double 表示双精度浮点数类型, 在16位、32位以及64位系统下都占用八个字节(Byte)。参考图1.4-4 双精度浮点数结构表,我们来分析实数的运算方式:
bit(位) | 63bit | (52-62)bit | (0-51)bit |
---|---|---|---|
数据描述 | 符号域 | 指数域 | 尾数域 |
尾数域:
数据范围0—51bit,共52位(bit),用来表示实数转换为二进制数字的部分,当转换后的二进制数字不够52位时,采用低位补0对齐。下面举个例子,实数5.25000012345:
整数部分:直接采用二进制转换得到 101;
小数部分:还是采用“乘2取整”运算,如果结果大于等于1取二进制数字1,否则取二进制数字0,然后用得到的结果小数部分继续运算,一直到小数部分为0或尾数域足够52位:
0.25000012345 * 2 = 0.5000002469,取0;
0.5000002469 * 2 = 1.0000004938,取1;
0.0000004938 * 2 = 0.0000009876,取0;
……
得到小数部分0100 0000 0000 0000 0000 0010 0001 0010 0011 0110 1011 0101 1101 1111 1111 1011 … ,整数和小数部分组合为 101.01 0000 0000 0000 0000 0000 1000 0100 1000 1101 1010 1101 0111… ;
尾数域完整数据:首先整数部分二进制数字01放入尾数域,然后把小数部分前50位二进制数字放入尾数域,得到
0101 0000 0000 0000 0000 0000 1000 0100 1000 1101 1010 1101 0111,一共52位有效二进制数字。
优化整数和小数部分组合:101.01 0000 0000 0000 0000 0000 1000 0100 1000 1101 1010 1101 0111,一共53位二进制数字。
二进制科学表示方式: 1.0101 0000 0000 0000 0000 0000 1000 0100 1000 1101 1010 1101 0111 * 2^2;
指数域:
数据范围52—62bit,共11位(bit),介码有效范围为1—2046,指数有效范围为-1022—1023。示例中,介码(M) = 1023 + 2 = 1025,二进制数字表示为 100 0000 0001。
符号域:
数据为63bit,占1位(bit),0表示正数,1表示负数。
bit(位) | 63bit | (52-62)bit | (0-51)bit |
---|---|---|---|
实数 5.25000012345 | 0 | 100 0000 0001 | 0101 0000 0000 0000 0000 0000 1000 0100 1000 1101 1010 1101 0111 |
参考图1.4-5 实数5.25000012345组成部分,我们得到二进制数字0100 0000 0001 0101 0000 0000 0000 0000 0000 1000 0100 1000 1101 1010 1101 0111,十六进制 0x4015 0000 0848 DAD7。
1.4.7 双精度浮点数取值范围
双精度浮点数由于拥有更多的存储空间,较于单精度浮点数而言,可以提供更精准的数据信息,双精度浮点数正数指数最大有效值是1023,得到正数最大值接近2 * 2^1023,VS2010编译器中为1.7976931348623158e+308;负数指数最小有效值是-1022,可以得到正数最小值接近2 * 2^-1022,VS2010编译器中为2.2250738585072014e-308。负数最大值接近 -2 * 2^-1022,负数最小值接近 -2 * 2^1023;在大部分编译器中,双精度浮点数可以表示包含小数在内的16-17位十进制数字。
1.5 本章总结
本章节,我们了解了C语言的基本类型和不同的类型运算方式,日常编程中,我需要做到正确选择类型、正确使用类型,这样可以避免数据存储造成的信息错误、程序异常崩溃等问题。本章节内容稍微有些复杂,需要多次学习才能完全理解,初期做到类型可以灵活使用就好,中后期再来回顾本章节,应该会有新的发现。
1.6 练习题
- 无符号短整型与有符号短整型有哪些区别?
- 使用十进制数字4096转换到二进制、八进制和十六进制。
- 参考Ascll码表,输出换行、空格和字符’A’。
- 短整型、整形和长整形分别占用几个字节?数字123456789使用哪个类型保存比较适合?
- 实数12.625转换到十六进制。
- 实数0xC0C8 1CA8 0000 0000转换到十进制。
目录预览
<一起学习C语言:C语言发展历程以及定制学习计划>
<一起学习C语言:初步进入编程世界(一)>
<一起学习C语言:初步进入编程世界(二)>
<一起学习C语言:初步进入编程世界(三)>
<一起学习C语言:C语言数据类型(一)>
<一起学习C语言:C语言数据类型(二)>