浮点数内存布局(精华版)

1.float和double的范围和精度

(1)float和double的表示范围是由指数的位数来决定的。float的指数位有8位,而double的指数位有11位,分布如下:
float:1bit(符号位)+8bits(指数位)+23bits(尾数位)
double:1bit(符号位)+ 11bits(指数位)+ 52bits(尾数位)

(2)在数学中,特别是在计算机相关的数字(浮点数)问题的表述中,有一个基本表达法:
 (浮点)数值 =      尾数    ×    底数 ^ 指数,(附加正负号)
于是,float的指数范围为-127~128(8bits(指数位)),而double的指数范围为-1023~1024(11bits(指数位)),并且指数位是按补码的形式来划分的。其中负指数决定了浮点数所能表达的绝对值最小的数;而正指数决定了浮点数所能表达的绝对值最大的数,也即决定了浮点数的取值范围(不代表全部位数有效)。float的范围为-2^128 ~ +2^128,也即-3.40E+38 ~ +3.40E+38;double的范围为-2^1024 ~ +2^1024,也即-1.79E+308 ~ +1.79E+308。

(3)float和double的精度(有效位数)是由尾数的位数来决定的。浮点数在内存中是按科学计数法来存储的,其整数部分始终是一个隐含着的“1”,由于它是不变的,故不能对精度造成影响。
float:2^23 = 8388608,共七位,意味着最多能有7位有效数字,但绝对能保证的为6位,也即float的精度为6~7位有效数字
double:2^52 = 4503599627370496,一共16位,同理,double的精度为15~16位。

 

2.C/C++对于浮点数的内存存储

对于浮点类型的数据采用 单精度类型(float)占用32bit / 双精度类型(double)占用64bit,我们在声明一个变量float f= 8.25f的时候,它是如何分配内存的呢?其实不论是float还是double在存储方式上都是遵从IEEE的规范的,float遵从的是IEEE R32.24 ,而double 遵从的是R64.53。

         无论是单精度还是双精度在存储中都分为三个部分(详见下图):
        1. 符号位: 0代表正,1代表为负
        2. 指数位: 用于存储科学计数法中的指数数据,并且采用移位存储
        3. 尾数部分:尾数部分

R32.24和R64.53的存储方式都是用科学计数法来存储数据的,比如8.25用十进制的科学计数法表示就为:8.25*10e0, 而120.5可以表示为:1.205*10e2 。但计算机根本不认识十进制数据,它只认识0/1,所以在计算机存储中,首先要将上面的数更改为二进制的科学计数法表示。

按照IEEE浮点数表示法,下面将把double型浮点数8.25转换为二进制代码。

把整数部和小数部分开处理:

①整数部分直接化二进制:1000(b)     //8(d)

②小数部分处理:

小数转换为二进制的方法:小数部分乘以2,取整数部分依次从左往右放在小数点后,直至小数点后为0。
       0.25 * 2 = 0.50 ... 0  //本例
       0.50 * 2 = 1.00 ... 1   //本例


/附加

小数转换为二进制的方法:小数部分乘以2,取整数部分依次从左往右放在小数点后,直至小数点后为0。
       0.91 * 2 = 1.82 ... 1
       0.82 * 2 = 1.64 ... 1
       0.64 * 2 = 1.28 ... 1
       0.28 * 2 = 0.56 ... 0
       0.56 * 2 = 1.12 ... 1
       0.12 * 2 = 0.24 ... 0
       0.24 * 2 = 0.48 ... 0
       0.48 * 2 = 0.96 ... 0
       0.96 * 2 = 1.92 ... 1
       0.92 * 2 = 1.84 ... 1
       0.84 * 2 = 1.68 ... 1
       0.68 * 2 = 1.36 ... 1
       0.36 * 2 = 0.72 ... 0
       0.72 * 2 = 1.44 ... 1
       0.44 * 2 = 0.88 ... 0
       0.88 * 2 = 1.76 ... 1
       0.76 * 2 = 1.52 ... 1
       0.52 * 2 = 1.04 ... 1
       0.04 * 2 = 0.08 ... 0
       0.08 * 2 = 0.16 ... 0
       0.16 * 2 = 0.32 ... 0       

/附加

 

所以直到加上前面的整数部分(1000 . 不要1,算3位,余下需凑够20位,隐藏位技术:最高位的1不写入内存)算够23位(尾数部分23位)就行了。

如果你够耐心,手工算到23位那么因该是:

8.25(d)=1000 . 01000000000000000000(b)     //(整数部分(1000 . 不要1))

120.5(d)=1111000 . 10000000000000000(b)    //(整数部分(1111000 . 不要1))

 

用二进制的科学计数法表示:

1000 . 01000000000000000000可以表示为1 .  00001000000000000000000 * 2e3,

1111000 . 10000000000000000可以表示为1 . 11100010000000000000000 * 2e6,

float有23bit的尾数部分,2^23=8388608,7位数表示不全,但能使float能精确到小数点后6位。

对于指数部分,因为指数可正可负,8位的指数位能表示的指数范围就应该为:-127-128,所以指数部分采用移位存储,存储的数据为元数据+127(double型则+1023)。

 

下面就看看8.25在内存中真正的存储方式:

0            10000010                        00001000000000000000000

符号位    127+3=130(指数部分)             尾数部分

 

下面就看看120.5在内存中真正的存储方式:

0             10000101                      11100010000000000000000

符号位    127+6=133(指数部分)             尾数部分

 

 

3.将一个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位。

举例:01101.1100011101 ----->1.1011100011101*(2^11)  则1011100011101存进尾数部分,11存进指数部分。

将一个内存存储的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”加上正号或负号即可。

 

4、为什么float从第7位开始失效?

举个例子,float ia=12345678.5;

按照前面的方法,用二进制分别表示整数部分和小数部分:101111000110000101001110 . 1

指数部分:小数点需左移23位变为:1 . 011110001100001010011101;//127+23=150

小数部分:011110001100001010011101,共有24位(不算小数点),但实际存储规定尾数部分(小数部分)只能存储23位。故将导致从第7位开始数字精度丢失。

 

参考网址:

https://blog.csdn.net/tom739/article/details/103577435

https://blog.csdn.net/sinat_38972110/article/details/82117072

https://www.cnblogs.com/little-white/archive/2013/08/04/3236493.html

https://blog.csdn.net/sunweiliang/article/details/82622038

 

 

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值