float浮点数的二进制存储方式及转换 下载本文

内容发布更新时间 : 2024/6/3 5:40:50星期一 下面是文章的全部内容请认真阅读。

float浮点数的二进制存储方式及转换

int和float都是4字节32位表示形式,为什么float的范围大于int? float精度为6~7位,且1.66×10^10的数字结果并不是166 0000 0000,指数越大,误差越大。

这些问题,都是浮点数的存储方式造成的。

float和double在存储方式上都遵从IEEE的规范,且float遵从的是IEEE R32.24,而double 遵从的是R64.53。

无论是单精度还是双精度在存储中都分为三个部分: 1. 符号位(Sign):0代表正,1代表为负;

2. 指数位(Exponent):用于存储科学计数法中的指数数据,并且采用了

移位存储;

3. 尾数部分(Mantissa):尾数部分; 其中float的存储方式如下图所示:

而双精度的存储方式为:

附注:将一个float型转化为内存存储格式的步骤

(1)先将这个实数的绝对值化为二进制格式。

(2)将这个二进制格式实数的小数点左移或右移n位,直到小数点移动到第一个有效数字的右边。

(3)从小数点右边第一位开始数出二十三位数字放入第22到第0位。 (4)如果实数是正的,则在第31位放入“0”,否则放入“1”。 (5)如果n 是左移得到的,说明指数是正的,第30位放入“1”;如果n是右移得到的或n=0,则第30位放入“0”。

(6)如果n是左移得到的,则将n减去1后化为二进制,并在左边加“0”补足七位,放入第29到第23位;如果n是右移得到的或n=0,则将n化为二进制后在左边加“0”补足七位,再各位求反,再放入第29到第23位。

此外,R32.24和R64.53的存储方式都是用科学计数法来存储数据的,比如8.25用十进制的科学计数法表示就为:8.25×10^0,而120.5可以表示为

1.205×10^2;而计算机根本不认识十进制的数据,它只认识0和1,所以在计算机存储中,首先要将上面的数更改为二进制的科学计数法表示,8.25用二进制表示可表示为1000.01,120.5用二进制表示可表示为1110110.1;我靠,不会连这都不会转换吧?那我估计要没辙了!

用二进制的科学计数法表示1000.01可表示为1.00001×2^3,而1110110.1则可表示为1.1101101×2^6,任何一个数的科学计数法都可表示为1.xxx×2^n;因此尾数部分就可表示为xxx,反正第一位都是1嘛,干嘛还要表示呀!?故可将小数点前面的1省略,故23bit的尾数部分,可以表达的精度却变成了24bit,道理就是在这里;那24bit能精确到小数点后的几位呢?我们知道,

2^24-1=16777215,按十进制来说是8位,故24bit就保证能使float可以精确到小数点后7位;另算上可以估读最后一位,故十进制具有8位有效数字。

而对于指数部分,因为指数可正可负,8位的指数位能表示的指数范围就应该为-127至128了,所以指数部分的存储采用移位存储,存储的数据为原数据加127,下面就看看8.25和120.5在内存中真正的存储方式。

首先看下8.25,用二进制的科学计数法表示为:1.00001×2^3。按照上面的存储方式,符号位为0,表示为正;指数位为3+127=130(二进制值10000010转十进制表示即为130);尾数部分为00001,故8.25的存储方式如下图所示:

而单精度浮点数120.5的存储方式如下图所示:

附注:将一个内存存储的float二进制格式转化为十进制的步骤:

(1)将第22位到第0位的二进制数写出来,在最左边补一位1,得到二十四位有效数字。将小数点点在最左边那个1的右边。

(2)取出第29到第23位所表示的值n,当30位是0时将n各位求反;当30位是1时将n增1。

(3)将小数点左移n位(当30位是0时)或右移n位(当30位是1时),得到一个二进制表示的实数。

(4)将这个二进制实数化为十进制,并根据第31位是0还是1加上正号或负号即可。

那么如果给出内存中一段数据,并且告诉你是单精度存储的话,你如何知道该数据的十进制数值呢?其实就是对上面的反推过程,比如给出如下内存数据:0100001011101101000000000000,首先我们现将该数据分段,0 10000 0101 110 1101 0000 0000 0000 0000,在内存中的存储就为下图所示:

根据我们的计算方式,可以计算出,这样一组数据表示为:1.1101101*2^6=120.5

而双精度浮点数的存储和单精度的存储大同小异,不同的是指数部分和尾数部分的位数。所以这里不再详细的介绍双精度的存储方式了,只将120.5的最后存储方式图给出,大家可以仔细想想为何是这样子的。

下面我就这个基础知识点来解决一个我们的一个疑惑,请看下面一段程序,注意观察输出结果:

float f = 2.2f;

double d = (double)f; printf(\ f = 2.25f; d = (double)f;

printf(\

可能输出的结果让大家疑惑不解,单精度的2.2转换为双精度后,精确到小数点后13位后变为了2.2000000476837,而单精度的2.25转换为双精度后,变为了2.2500000000000,为何2.2在转换后的数值更改了而2.25却没有更改呢?很奇怪吧,其实通过上面关于两种存储结果的介绍,我们已经大概能找到答案。

首先我们看看2.25的单精度存储方式,很简单为0 1000 0001 001 0000 0000 0000 0000 0000,而2.25的双精度表示为0 100 0000 0001 0010 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000,这样2.25在进行强制转换的时候,数值是不会变的。

而我们再看看2.2呢,2.2用科学计数法表示应该为:将十进制的小数转换为二进制的小数的方法为将小数不断乘以2,并取整数部分,所以0.282=0.4,故二进制小数第一位为0.4的整数部分0;接下来0.4×2=0.8,第二位为0;0.8×2=1.6,第三位为1;0.6×2 = 1.2,第四位为1;0.2×2=0.4,第五位为