C 语言

关注公众号 jb51net

关闭
首页 > 软件编程 > C 语言 > C语言浮点数的表示与储存

C语言之浮点数的表示与储存方式

作者:七月不远.

这篇文章主要介绍了C语言之浮点数的表示与储存方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教

1. 二进制小数

1.1 十进制小数的表示方法

理解浮点数的第一步是考虑含有小数值的十进制数字

先来看一下十进制数字的表示法:

其中每个十进制数 的取值范围是0~9。这个表达描述的数值 的定义如下:

数字权的定义与十进制小数点符号( ‘.’ ) 相关,这意味着小数点左边的数字的权是10的正幂,得到整数值,而小数点右边的数字的权是10的负幂,得到小数值。例如十进制数12. 34表示数字

1.2 二进制小数的表示方法

类比十进制数,二进制表示小数可以用如下表示法表示,

以图片表示为:

其中 的取值范围是0和1。这个表达描述的数值 的定义如下:

符号 ‘ . ’ 现在变为了二进制的点,点左边的位的权是2的正幂,点右边的位的权是2的负幂。例如,二进制数101.11表示数字

2. IEEE浮点表示

2.1 IEEE浮点标准

上一节提到的定点表示法并不能很有效地表示非常大的数字。IEEE(电气和电子工程师协会)浮点标准用如下的形式来表示一个数:

在这个式子中设计三个变量,s, M以及E

符号 (sign) : s决定这数是负数 (s = 1) 还是正数 (s = 0)尾数 (ignificand) : M是一个二进制小数阶码 (exponent): E的作用是对浮点数加权,这个权重是2的E次幂(可能是负数)

例如:

5.0 化为二进制小数,由于 5.0 为正数,因此 s = 0,M = 1.01,E = 2

这种标准将浮点数封装成一种似乎很难理解的形式来存储,但其实是相当优雅的。

2.2 单精度和双精度浮点数的封装形式

在C语言中,浮点数分为单精度浮点数 float 和双精度浮点数 double,其中float在内存中占4个字节,32个比特位;double在内存中占8个字节,64个比特位。

不管是 float 还是 double ,它们都被分为三个字段,分别用来表示符号位s,阶码字段exp和编码尾数frac。这三个字段与上述的符号s,尾数M,阶码E一一对应,但并非将s,M,E直接存入内存,而是根据浮点数的不同数值类型按照不同的规则进行编码。

2.3 浮点数的数值分类

根据阶码的不同,浮点数的数值可以分为三类:

exp的值决定了这个数属于上面类型中的哪一种,以float类型为例:

2.3.1 规格化的值 (Normalized Values)

当阶码域不全为0并且不全为1时,表示该数值为规格化的值

当阶码字段不全为0时,表示该数值为非规格化的值。这是一种最普遍的情况,大多数浮点数都属于这类。比如上一小节的5.0

对于规格化的值,在得到s, E, M之后还需要进行一些处理才能放进内存:

规则1:

规则2

到这里我们可以解决 5.0 这样一个规格化的值的存放问题了:

我们知道,对于5.0来说,s = 0,M = 1.01,E = 2,

根据以上规则,在内存中,符号位字段s = 0,阶码字段exp = E + 127 = 129 = 10000001,编码尾数f = 01000000000000000000000(不够23位要在后面补0)

结合来看:

0 10000001 01000000000000000000000,化为十六进制为40 a0 00 00

在小端机器上的结果为:

2.3.2 非规格化的值 (Denormalized Values)

当阶码域为全0时, 所表示的数是非规格化的值

在这种情况下,规则又有所不同:

规则1

规则2

非规格化数有两个用途:

2.3.3 特殊值 (Special Values)

当阶码域为全1时, 所表示的数是特殊值

特殊值可分为两种:

1. 当小数域全为0 时,得到的值表示无穷,当 s = 0 时,是+∞, 或者当 s = 1时,是 -∞

2.当小数域为非零时, 结果值被称为 “NaN”(Not a Number)。一些运算的结果不能是实数或无穷, 就会返回这样的NaN值

3. 数字示例

为了更加直观地理解,我们用一个8位浮点数的例子,假定符号位 s 的长度为1,阶码字段的长度为4,小数字段的长度为3:

对于非规格数:

对于规格数

可以看到,从最大非规格数0 0000 111到最小规格数0 0001 000这样的过渡是很自然的,体现出了上述IEEE标准的逻辑性与和谐的美感

4. 舍入

因为表示方法限制了浮点数的范围和精度, 所以浮点运算只能近似地表示实数运算

我们企图找到一个与值 最相近的值匹配值 来作为储存的值

例如

如果由于表示方法的限制,1.5这样一个值无法完全放在内存中,需要舍掉小数点后的值,那么舍入结果是1还是2呢❔

IEEE定义了四种舍入方式:

其中,向偶数舍入(round - to - even)又被称为向最接近的值舍入(round - to - nearest),是默认的方式,试图找到一个最接近的匹配值

向上和向下舍入很好理解,一个介于1~2之间的数如 1.5 向上舍入是2,向下舍入是1

向零舍入是指在在数轴上的数向 0 的方向进行舍入,比如 1.50 向零舍入会找到 1 和 2 之间更靠近0 的数 1 ,-1.50 向零舍入会找到 -1 和 -2 之间更靠近 0 的数 -1

向偶数舍入,指当一个数是两个可能结果的中间数时,它将数字向上或者向下舍入,使得结果的最低有效数字是偶数

比如1.50可以向1舍入,也可以向2舍入,并且正好是1和2的中间值,这时会默认向2(偶数)舍入;比如2.50可以向2舍入,也可以向3舍入,并且正好是2和3的中间值,这时同样会向2(偶数)舍入。

值得注意的是:

向偶数舍入只针对那些“中间值”。当值为1.49或1.51时,依旧舍入为 1 和 2

方式1.401.601.502.50-1.50
向偶数舍入1222-2
向零舍入1112-1
向上舍入1112-2
向下舍入2223-1

为什么要使用向偶数舍入呢?

使用其他三种舍入方法,在一组数据中很容易引入平均值的统计偏差 ,当1.50 1.60 1.70这样一组数据都使用向上舍入,结果是2 2 2,平均值会偏大

向偶数舍入在大多数现实情况中避免了这种统计偏差。在50%的时间里,它将向上舍入,而在50%的时间里,它将向下舍入

对二进制的浮点数舍入同样遵循向偶数舍入的原则,并将 0 视为偶数,1 视为奇数

例如:

10.11100 ,当舍入需要精确到小数点后两位时, 后三位100代表 ,正好是中间值,因此向偶数舍入为11.00

5. 浮点运算

考虑下面几个式子:

6. C语言中的浮点数

所有的C语言版本提供了两种不同的浮点数据类型: floatdouble

当int ,float,double不同数据类型之间进行强制类型转换时,得到的结果可能会超出我们的预期,程序改变数值和位模式的原则如下( 假设int是32位的) :

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

您可能感兴趣的文章:
阅读全文