您的位置:首页 > 其它

float内存存储方式

2013-03-01 20:39 148 查看
任何数据在内存中都是以二进制(1或着0)顺序存储的,每一个1或着0被称为1位,而在x86CPU上一个字节是8位。比如一个16位(2字节)的 short int型变量的值是1156,那么它的二进制表达就是:00000100 10000100。由于Intel CPU的架构是Little Endian(请参数机算机原理相关知识),所以它是按字节倒序存储的,那么就因该是这样:10000100 00000100,这就是定点数1156在内存中的结构。

那么浮点数是如何存储的呢?目前已知的所有的C/C++编译器都是按照 IEEE(国际电子电器工程师协会)制定的IEEE 浮点数表示法来进行运算的。这种结构是一种科学表示法,用符号(正或负)、指数和尾数来表示,底数被确定为2,也就是说是把一个浮点数表示为尾数乘以2的指数次方再加上符号。下面来看一下具体的float的规格:

float

共计32位,折合4字节

由最高到最低位分别是第 31、30、29、……、0位

31位是符号位,1表示该数为负,0反之。

30-23位,一共8位是指数位。

22-0位,一共23 位是尾数位。

每8位分为一组,分成4组,分别是A组、B组、C组、D组。

每一组是一个字节,在内存中逆序存储,即:DCBA

我们先不考虑逆序存储的问题,因为那样会把读者彻底搞晕,所以我先按照顺序的来讲,最后再把他们翻过来就行了。

现在让我们按照IEEE浮点数表示法,一步步的将float型浮点数12345.0f转换为十六进制代码。在处理这种不带小数的浮点数时,直接将整数部转化为二进制表示:1 11100010 01000000也可以这样表示:11110001001000000.0然后将小数点向左移,一直移到离最高位只有1位,就是最高位的 1:1.11100010 01000000一共移动了16位,在布耳运算中小数点每向左移一位就等于在以2为底的科学计算法表示中指数+1,所以原数就等于这样:1.11100010010000000 * (
2 ^ 16 )好了,现在我们要的尾数和指数都出来了。显而易见,最高位永远是1,因为你不可能把买了16个鸡蛋说成是买了0016个鸡蛋吧?所以这个1我们还有必要保留他吗?没有!好的,我们删掉他。这样尾数的二进制就变成了:11100010 010000000 最后在尾数的后面补0,一直到补够23位:11100010 01000000 0000000

再回来看指数,一共8位,可以表示范围是0 - 255的无符号整数,也可以表示-128 - 127的有符号整数。但因为指数是可以为负的,所以为了统一把十进制的整数化为二进制时,都先加上127,在这里,我们的16加上127后就变成了 143,二进制表示为:10001111

12345.0f这个数是正的,所以符号位是0,那么我们按照前面讲的格式把它拼起来:

0 10001111(指数) 1110001 00100000 00000000(尾数)

0 1000111 11110001 00100000 00000000

再转化为16进制为:47 F1 20 00,最后把它翻过来,就成了:00 20 F1 47。

现在你自己把54321.0f转为二进制表示,自己动手练一下!

有了上面的基础后,下面我再举一个带小数的例子来看一下为什么会出现精度问题。

按照IEEE浮点数表示法,将 float型浮点数123.456f()转换为十六进制代码。对于这种带小数的就需要把整数部和小数部分开处理。整数部直接化二进制:100100011。小数部的处理比较麻烦一些,也不太好讲,可能反着讲效果好一点,比如有一个十进制纯小数0.57826,那么5是十分位,位阶是1/10;7是百分位,位阶是1/100;8是千分位,位阶是1/1000……,这些位阶分母的关系是10^1、10^2、10^3……,现假设每一位的序列是{S1、S2、 S3、……、Sn},在这里就是5、7、8、2、6,而这个纯小数就可以这样表示:n
= S1 * ( 1 / ( 10 ^ 1 ) ) + S2 * ( 1 / ( 10 ^ 2 ) ) + S3 * ( 1 / ( 10 ^ 3 ) ) + …… + Sn * ( 1 / ( 10 ^ n ) )。把这个公式推广到b进制纯小数中就是这样:

n = S1 * ( 1 / ( b ^ 1 ) ) + S2 * ( 1 / ( b ^ 2 ) ) + S3 * ( 1 / ( b ^ 3 ) ) + …… + Sn * ( 1 / ( b ^ n ) )

float 内存如何存储的

类型
[align=center]存储位数[/align]
总位数
偏移值

(offset)
[align=left]数符(S)[/align]
[align=left]阶码(E)[/align]
[align=left]尾数(M)[/align]
[align=left]短实数(float) [/align]
[align=left] 1 [/align]
[align=left] 8[/align]
[align=left] 23[/align]
[align=left] 32[/align]
[align=left]127[/align]
[align=left]长实数(double)[/align]
[align=left] 1[/align]
[align=left] 11[/align]
[align=left] 52[/align]
[align=left] 64[/align]
[align=left] 1023[/align]
N (10) = 123.456,

换算成二进制表示:
N (2) = 1111011. 01110100101111001
= 1. 11101101110100101111001(...) * 2^6

那么E – 127 = 6; E = 127 + 6 = 133(10) = 10000101(2)

M = 111 0110 1110 1001 0111 1001 (省略了最高数字位1,
共23bit)


组合起来就是:

S E M
0 10000101 111 0110 1110 1001 0111 1001
4bit一间隔:
0100 0010 1111 0110 1110 1001 0111 1001
4 2 F 6 E
9 7 9


转载自: http://blog.163.com/laorenyuhai126@126/blog/static/193507792010103104341148/
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------

最为一个常识,我们都知道浮点型在内存中占用的是4个字节的空间,而long型占用的是8个字节的空间。可是为什么4个字节的float型的最大值会大于long型的最大值呢?
我们都知道,float类型的范围是:一3.403E38~3.403E38。而long类型的范围是:-2^63~2^63-1(大概是9*10^18)。

我以前也是简单的记住就算完事了,对于它为什么会这样却没有考虑过。

下面给大家分享一下我现在的理解:

long整型数,在内存中占用8个字节共64位,它表示的数值有2的64次方,平分正负,数值范围是负2的63次方到正2的63次方-1。
而float在内存中占4个字节,共32位,但是浮点数在内存中是这样的:
V=(-1)^s * M * 2^E



浮点数的32位不是简单的直接表示大小,而是按照一定的标准分配的。
其中第1位,符号位,即S。
接下来的8位,指数域,即E。
剩下的23位,小数域,即M,M的取值范围为[1,2)或[0,1)。
也就是说,浮点数在内存中的二进制值不是直接转换为十进制数值的,而是按照上述公式计算而来,通过这个公式,虽然只用到了4个字节,但是浮点数却比长整型的最大值要大。

这也就是为什么在数据转换的时候,long类型转换为float类型的根本原因所在!

转载自:http://blog.csdn.net/lbq613613/article/details/7896621
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: