概述
1. 范围
float和double的范围是由指数的位数来决定的。
float的指数位有8位,而double的指数位有11位,分布如下:
float:
1bit(符号位) 8bits(指数位) 23bits(尾数位)
double:
1bit(符号位) 11bits(指数位) 52bits(尾数位),如下图所示。
于是,float的指数范围为-127~+128,而double的指数范围为-1023~+1024,并且指数位是按补码的形式来划分的。
其中负指数决定了浮点数所能表达的绝对值最小的非零数;而正指数决定了浮点数所能表达的绝对值最大的数,也即决定了浮点数的取值范围。
float的范围为-2^128 ~ +2^128,也即-3.40E+38 ~ +3.40E+38;double的范围为-2^1024 ~ +2^1024,也即-1.79E+308 ~ +1.79E+308。
2. 精度
float和double的精度是由尾数的位数来决定的。浮点数在内存中是按科学计数法来存储的,其整数部分始终是一个隐含着的“1”(后面会讲到),由于它是不变的,故不能对精度造成影响。
float:2^23 = 8388608,一共七位,这意味着最多能有7位有效数字,但绝对能保证的为6位,也即float的精度为6~7位有效数字;如果此处不明白可以看一下此处说明:假设有一个十进制小数0.000001,它为6位数,转化为二进制则需不断的乘2取整,把0.暂且不看,只看000001,不断乘2,有多少个2才能达到6位数字的长度呢?答案为19个时刚好能达到6位,大小为524288,显然没有包含所有的6位数字,因而当达到20个2时,达到1048576七位数了,显然可以包含所有的6位数了,因而说绝对精确到小数点后6位;而float所给的尾数部分为23位,2^23=8388608,依然没有超过7位,所以说最多有7位有效数字。double同理。
double:2^52 = 4503599627370496,一共16位,同理,double的精度为15~16位
下面以float为例说明十进制float转与二进制float之间的转换原理
浮点型变量在计算机内存中占用4字节(Byte),即32-bit。遵循IEEE-754格式标准。
一个浮点数由2部分组成:底数m 和 指数e。
±mantissa × 2exponent 公式中的mantissa 和 exponent使用二进制表示
底数部分 使用2进制数来表示此浮点数的实际值。
指数部分 占用8-bit的二进制数,可表示数值范围为0-255。
但是指数应可正可负,所以IEEE规定,此处算出的次方须减去127才是真正的指数。所以float的指数可从 -127到128.
底数部分实际是占用24-bit的一个值,由于其最高位始终为 1 ,所以最高位省去不存储,在存储中只有23-bit。为什么最高位会始终为1呢?看此处说明:
{
9的二进制表示为1001
0.125的二进制表示为0.001
所以91.25的表示成1001.001
在计算机中,任何一个数都可以表示成1.xxxxxx*2^n 这样的形式,
其中xxxxx就表示尾数部分,n表示指数部分
其中,因为最高位橙色的1这里,由于任何的一个数表示成这种形式时这里都是1,所以在存储时实际上并不保存这一位,这使得float的23bit的尾数可以表示24bit的精度,double中52bit的尾数可以表达53bit的精度。
}
到目前为止, 底数部分 23位 加上指数部分 8位 使用了31位。那么前面说过,float是占用4个字节即32-bit,那么还有一位是干嘛用的呢? 还有一位,其实就是4字节中的最高位,用来指示浮点数的正负,当最高位是1时,为负数,最高位是0时,为正数。
浮点数据就是按下表的格式存储在4个字节中:
Address+0 Address+1 Address+2 Address+3
计算机内float数据存储格式:(对应图中float)
SEEE EEEE EMMM MMMM MMMM MMMM MMMM MMMM
S: 符号位,表示浮点数正负,1为负数,0为正数
E: 指数位,指数加上127后的值的二进制数
M: 尾数位,24-bit的底数(只存储23-bit)
主意:这里有个特例,浮点数 为0时,指数和底数都为0,但此前的公式不成立。
因为2的0次方为1,所以,0是个特例。当然,这个特例也不用认为去干扰,编译器会自动去识别。
通过上面的格式,我们下面举例看下-12.5在计算机中存储的具体数据:
Address+0 Address+1 Address+2 Address+3
0xC1 0x48 0x00 0x00
接下来我们验证下上面的数据表示的到底是不是-12.5,从而也看下它的转换过程。
由于浮点数不是以直接格式存储,他有几部分组成,所以要转换浮点数,首先要把各部分的值分离出来。
Address+0 Address+1 Address+2 Address+3
格式 SEEEEEEE EMMMMMMM MMMMMMMM MMMMMMMM
二进制 11000001 01001000 00000000 00000000
16进制 C1 48 00 00
可见:
S: 为1,是个负数。
E:为 10000010 转为10进制为130,130-127=3,即实际指数部分为3.
M:为 10010000000000000000000。 这里,在底数左边省略存储了一个1,使用 实际底数表示为 1.10010000000000000000000
到此,我们把三个部分的值都拎出来了,现在,我们通过指数部分E的值来调整底数部分M的值。
调整方法为:
如果指数E为负数,底数的小数点向左移,如果指数E为正数,底数的小数点向右移。小数点移动的位数由指数E 的 绝对值决定。
这里,E为正3,使用向右移3为即得:1100.10000000000000000000
这个结果就是12.5的二进制浮点数,将他换算成10进制数就看到12.5了,
如何转换,看下面:
小数点左边的1100 表示为 (1 × 2^3) + (1 × 2^2) + (0 × 2^1) + (0 × 2^0), 其结果为 12 。
小数点右边的 .100… 表示为 (1 × 2^-1) + (0 × 2^-2) + (0 × 2^-3) + ... ,其结果为.5 。
以上二值的和为12.5, 由于S 为1,使用为负数,即-12.5 。
所以,16进制 0XC1480000 是浮点数 -12.5 。
上面是如何将计算机存储中的二进制数如何转换成实际浮点数,下面看下如何将一浮点数装换成计算机存储格式中的二进制数。
举例将17.625换算成 float型。
首先,将17.625换算成二进制位:10001.101 ( 0.625 = 0.5+0.125, 0.5即 1/2, 0.125即 1/8 )
再将 10001.101 小数点向左移,直到小数点前只剩一位 成了 1.0001101 x 2的4次方(因为右移了4位)。此时 我们的底数M和指数E就出来了:
底数部分M,因为小数点前必为1,所以IEEE规定只记录小数点后的就好,所以此处底数为 0001101 。
指数部分E,实际为4,但须加上127,固为131,即二进制数 10000011
符号部分S,由于是正数,所以S为0.
综上所述,17.625的 float 存储格式就是:
0 10000011 00011010000000000000000
转换成16进制:0x41 8D 00 00
所以,一看,还是占用了4个字节。
实际上在X86计算机中,采用的是小端存储方式,即低地址存储低位数据,高地址存储高位数据。
(10应该为8D,拷贝的图,没改)
此贴参考一下三处帖子,其中叙述有错误的地方已改正。如本帖有错,还请指正。
http://blog.sina.com.cn/s/blog_8a18c33d01013bke.html
http://blog.csdn.net/guqsir/article/details/7015267
http://www.cnblogs.com/BradMiller/archive/2010/11/25/1887945.html
最后
以上就是俊逸钢笔为你收集整理的float和double数据在计算机中的存储方式的全部内容,希望文章能够帮你解决float和double数据在计算机中的存储方式所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复