数据的存储(2) — 浮点数存储 & 浮点数比较大小

  • Post author:
  • Post category:其他



目录


一、浮点数定义与特点


二、浮点数的存储与读取


三、浮点数存储带来的特性


1. 比较大小


2. 浮点数相减


一、浮点数定义与特点

  既有整数部分、又有小数部分的实数浮点数在计算机中用以近似表示任意某个实数。浮点计算是指浮点数参与的运算,这种运算通常伴随着因为无法精确表示而进行的近似或舍入。
  常见的浮点数∶3.14159 ,1E10,浮点数家族包括:float、double、long double类型。 

二、浮点数的存储与读取

整数是以补码(通过原码求出补码)的形式存储在内存中。那浮点数9.1234567应该怎么存呢?也按补码存吗?如果是的话,整数是直接出原码,但浮点数的小数点该怎么处理?

根据国际标准IEEE(电气和电子工程协会) 754,任意一个二进制浮点数V可以表示成下面的形式:

🙋🏻‍♂️举例来说:

十进制的5.0,写成二进制是 101.0 ,相当于 1.01×2^2 。那么,按照上面V的格式,可以得出s=0,M=1.01,E=2。

十进制的-5.0,写成二进制是 -101.0 ,相当于 -1.01×2^2 。那么,s=1,M=1.01,E=2。


类似于科学表示法。1<=M<2,M写成 1.xxxxxx 的形式,其中xxxxxx表示小数部分。






IEEE 754规定:

对于32位的浮点数,最高的1位是符号位s,接着的8位是指数E,剩下的23位为有效数字M

对于64位的浮点数,最高的1位是符号位S,接着的11位是指数E,剩下的52位为有效数字M。

IEEE 754对有效数字M和指数E,还有一些特别规定。对于有线数字M:

由于1<=M<2,M总是可以写成1.xxx*10^n 。

IEEE 754规定,在计算机内部保存M时,默认这个数的第一位总是1,因此

存储时不存这个1,只存储后面的xxx部分。

比如保存1.01的时候,只保存01,等到读取的时候,再把第一位的1加上去。

这样做的目的,是节省1位有效数字。以32位浮点数为例,留给M只有23位,将第一位的1舍去以后,等于可以保存24位有效数字。

至于指数E,情况就比较复杂:


首先,规定E为一个无符号整数(unsigned int)


这意味着,如果E为8位,它的取值范围为0~255;如果E为11位,它的取值范围为0~2047。但是,我们知道,科学计数法中的E是可以出现负数的。所以IEEE 754规定,

存入内存时E的真实值必须再加上一个中间数

。对于8位的E,这个中间数是127;对于11位的E,这个中间数是1023。

比如,2^-1的E是-1。所以保存成32位浮点数时,必须保存成-1+127=126:即01111110。






然后,指数E从内存中取出还可以再分成三种情况:






E不全为0或不全为1

这时,浮点数就采用下面的规则表示:


即指数E的计算值减去127(或1023),得到真实值,再将有效数字M前加上第一位的1。


比如:

0.5(1/2)的二进制形式为0.1,由于规定正数部分必须为1,即将小数点右移1位,则为

1.0*2^(-1),其阶码为-1+127=126,表示为01111110,而尾数1.0去掉整数部分为0,补齐0到23位00000000000000000000000,则其二进制表示形式为:

0 01111110 00000000000000000000000


这个时候是:怎么存就怎么取






E全为0

这时,

浮点数的指数E等于1-127(或者1-1023)即为真实值,


有效数字M不再加上第一位的1,而是还原为0.xxxxxx的小数。这样做是为了表示±0,以及接近于0的很小的数字。


此时:存储在内存中的E是



加了



中间数



127的指数



。加了127变成的全为0,说明原来的数是个负数,而且是个比较大的负数。-> 指数很小作为分母,浮点数很小。






E全为1

这时,

如果有效数字M全为0,表示±无穷大(正负取决于符号位s)



此时:存储在内存中的E是



加了



中间数



127的指数



。加了127变成的全为1,说明原来的数也是个比较大的整数,-> 指数很大,浮点数大。

三、浮点数存储带来的特性

先解释一下开头的一句话: 浮点数在计算机中用以


近似表示


任意某个实数。

举例1:25.875

存储时,先将整数部分和小数部分化为二进制。结果为11001.111

举例2:25.873

整数部分同理,但是小数部分会一直循环。但是我们知道不管是32位还是64位,小数部分只有那么多空间会有精度限制。对于超出了精度限制的浮点数,计算机会把它们的精度之外的小数部分截断。这个时候只能近似表示25.873。

1. 比较大小


不可将浮点变量用“==”或“!=”与任何数字比较。



由于有些数是近似存储,计算机会把它们的精度之外的小数部分截断。

例如:  float a=10.222222225,b=10.222222229 ,数学上a和b是不相等的,但在32位计算机中它们是相等的。



整数9和浮点数9.0,在数学意义上相等。但是由于存储机制不同,计算机会认为他们不等。

那应该怎么比较浮点数大小呢?


定义一个精度,用差的绝对值比较,差值在精度范围内就认为是相等的。所以两个浮点数进行比较时,应设法转化成“>=”或“<=”形式。

代码示例:

#include <stdio.h>
#include<math.h>
#define EPS 1e-7 //判断浮点数是否位于0的一个很小的邻域内[-EPS,EPS]内
int main() {
    //判断一个浮点数是否等于0
    float a;
    scanf("%f", &a);
    if (fabs(a) <= EPS) { printf("=0"); }    
    else if (a > EPS) { printf(">0"); }
    else { printf("<0"); }

     //比较两个浮点数大小
    float a, b;
    scanf("%f %f", &a, &b);
    if (fabs(a - b) <= EPS) { printf("a=b"); }
    else if ((a - b) > EPS) { printf("a>b"); } 
    else { printf("a<b"); }           

    return 0;
}

2. 浮点数相减

再接着解释开头的一句话:浮点计算是指浮点数参与的运算,这种运算通常伴随着因为


无法精确表示而进行的近似或舍入


在计算机中,加减法运算用

补码

实现。

算术运算的常识:两个浮点数如果要进行加减法运算,它们的

阶或者指数必须相等

浮点数运算比较复杂,涉及到对阶、位数求和、规格化、舍入、溢出判断等步骤。 暂时记住如果两个浮点数比较大,直接相减的话,浮点数损失的精度多。所以通常我们不用浮点数相减判断大小,而是用>或者<比较大小。



版权声明:本文为weixin_51626258原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。