C标准库——浮点数
浮点数格式
学C的时候就知道,浮点数采用的是类似于科学计数法的表示方式。具体的浮点数的模型是:
一个尾数(mantissa),一个基数(base),一个指数(exponent)和符号位表示。
再百度的深一点,在规范化形式下(没错,还有非规范形式),就可以把float和double的表示格式列个表格:
类型 |
存储位数 |
偏移值 |
||||
|
符号位(s) |
指数位(e) |
尾数位(m) |
总位数 |
十六进制 |
十进制 |
单精度 float |
1位 |
8位 |
23位 |
32位 |
0x7FH |
+127 |
双精度 double |
1位 |
11 位 |
52位 |
64位 |
0x3FFH |
+1023 |
不过有些细节是不得不说的:
基数 ———— 这个基数是可以用户定义的,一般是2,但取10,16也是可以的。如何指定呢?
在头文件float.h中定义了宏FLT—RADIX,它的值就是选定的基数。
尾数 ———— 这是个定点小数,采用原码存储。尾数的最左端隐含了1,也就是说23位全部用来表示小数,而实际表示的数是24位精度。
指数 ———— 这当然是整数,不过采用移码存储。为了处理负指数的情况,要把实际指数值加上一个偏移值作为保存值(float为127;double为1023)。对单精度浮点而言,有效的指数范围是 -126~127(保存值为1~254,这是因为两个端点值用作特殊值表示)。
IEEE 754定义了什么
只要一说到浮点数,这个标准是不能不提的。那么这个标准主要规定了哪些东西呢?
A、浮点数的格式:
除了上面提到的两种基本的浮点格式,还定义了这两种格式的扩展。
但标准只规定扩展格式的最小精度和大小。例如,IEEE 双精度扩展格式必须至少具
有64位有效数字,并总共占用至少79 位。
B、4种浮点数舍入方式:
-
舍入到最接近:将结果舍入为最接近且可以表示的值(默认)。这种方式下,采取的向偶数舍入,
与我们熟悉的'四舍五入'不同。这主要是为了处理中值(两个相邻整数的平均值),如果这些值都遵循'五入',
那么在数值计算中会累积较大误差。也就是说——— 0.5要舍到0,1.5要入到2 ———以保证50%的概率。
-
朝+∞方向舍入:将结果朝正无限大(向上)的方向舍入。
-
朝-∞方向舍入:将结果朝负无限大(向下)的方向舍入。
-
朝0方向舍入:将结果朝0的方向舍入。忽略小数部分。
这些舍入方式是可以由float.h中的宏FLT—ROUNDS指定的。
C、5种浮点异常:
-
无效运算: 默认返回NaN(如对负数开平方时)
-
被零除: 默认返回负无穷或正无穷
-
上溢: 默认返回正负无穷大(结果数据太大无法表示时)
-
下溢: 默认返回非规范数(结果数据太小无法表示时)
-
不准确: 默认返回舍入后的正确值
如果产生异常,就会影响异常向量表,有些还会产生中断。至于中断,异常处理当然不是本文要讨论的。
D、特殊值:(还记得指数部分没有用到的两个端点值么?)
-
NaN(Not a Number):对于单精度浮点数,NaN 表示为指数为128(保存值=255),且尾数不等于零的浮点数。
-
± ∞: 这个数的定义和NaN一样,不过它的尾数一定为0.(用于大出范围的数)
-
± 0: 对单精度而言,0表示为指数域为-127(保存值=0),尾数域为全为0。
-
非规范化数: 这个数的定义和有符号0一样,不过尾数不能为0.(用于小出范围的数)
这个标准规范的东西当然不止这些,还有一些关于运算准确度要求、不同类型数据间转换及不同进制数据间的转换等等。
其实这里谈到的是浮点数最老的标准,新标准应该也有几个了吧,最好的资料当然就是这些标准的文档,倘若有时间,仔细看看还是有好处的。
库文件float.h
最初很多处理器在硬件方面并不支持浮点运算,因为这会增加不少成本,不支持浮点运算,会使微处理器设计的复杂度减半。之所以需要浮点运算,是因为科学计算等一些高精度运算的需要的存在。float.h这个文件的构造当然是要遵循这个标准的,不过这个头文件并有太多的东西。除了前面提到过的两个宏,这个头文件还定义了一些关于浮点数取值范围的宏。
其实也没有特别的理由要来认识这个库,毕竟即使是做数值计算的算法,也可以用MATLAB 这个彪悍的工具。不过,在了解了这个头文件后,约定整数范围的limits.h就很容易看清楚了。不过这也仅限于认识这个库,至于它要怎么实现就又是另一个层次的问题了。
参考文章:定点数与浮点数的区别。(文章不错,虽然有些小错误)