浮点数在内存中的存储
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了浮点数在内存中的存储相关的知识,希望对你有一定的参考价值。
关于整型在内存中的存储,上一篇已经讲过主要内容了,这里再补充一些需要注意的点。
整型在内存中存储的是补码,当我们在写无符号整型变量的时候,放在循环的继续条件判断时,如果没有考虑清楚无符号类型表示范围很容易出错或导致死循环。例如:
#include <stdio.h>
//这道题没有考虑无符号char的表示范围就很容易出错
int main()
unsigned char a[1000];
int i;
for(i=0; i<1000; i++)
a[i] = 1+i;//遍历数组
printf("%d", strlen(a));
return 0;
解析:定义了一个有1000个元素,每个元素都是无符号char类型,通过循环给每个元素赋值。如果我们没有考虑到范围的话,你可能会答出随机值的答案,因为你觉得在整个数组里面是找不到\\0或数值0的。这就是这道题考的地方,我们画个图就清晰了。
从1开始,顺时针递增下去,当走到255时,再加1,由于256不在char表示范围内,它会被判断为0存入,看图即可理解。所以答案是255而非随机值。
上面这道是范围导致的错误,下面这道是范围导致的死循环:
#include <stdio.h>
int main()
unsigned int i;
for(i=9; i>=0; i--)
printf("hello,world\\n");
return 0;
这里的坑就是,正常来讲,我们在当i逐渐递减达到0的时候,打印出9个hello,world后,i经过调整变为-1不符合继续执行的条件就跳出循环了。请注意!!这是unsigned int i。i = -1,整型常量-1的补码是32位全1,存入i中,最高位在无符号i看来就是有效位1,所以i是一个32位全1的超大数,肯定符合继续条件啦,也就是说,无符号i无论如何都是>=0的,判断条件恒成立,循环不停,死循环。
*在这里有个如何看待内存视角的先后顺序,-1是整型常量,补码是全1,放入i中,最高位不再是符号位,也就是最高位1不表示“负”的含义,变成正数了,整数原反补相同,全1补码就是全1原码。
OK,回到本篇主题,浮点数在内存中的存储0.0。
n以整型的形式存放整型常量9,第一个打印没问题,第二个打印就不清楚来路了,通过指针解引用以浮点型给n存放浮点型常量9.0,第三个打印不是我们懂的,第四个是正常的。可以看出,以整型(浮点型)存放,用整型(浮点型)取出是符合预期的,而整型存放,浮点型取出或相反都是存在问题的,这是因为,整型和浮点型的存取不一样导致的。
根据国际标准IEEE(电子和电气工程协会)754规定,任意一个浮点数可以用
(-1)^S * M *2^E表示,S的值是0或1,M是有效位,E是指数。
eg:5.0的表示形式。
第一步:用二进制分别表示小数点前后的数--- 101.0
第二步:用科学计数法来表示--- 1.010*2^2 ,这里的1.010相当于M,2的阶乘2是E,我们可以类比十进制的科学计数比如100(一百)可以写成1.0*10^2。
S就是看5.0是真还是负来给就0或1就可以了,S是0就表示浮点数是正,相反1就是负。
确定SEM后,我们还需要知道,在内存中是怎么给SEM分配空间的,S只表示0或1,只需要一个bit位就足够了。IEEE标准规定,float类型(总空间是4个字节),S占1个bit,E占8个bit,M占23个bit。double类型(总空间是8个字节),S一样,E占11个,M占52个。
E的存法:IEEE规定E是一个无符号数,但我们知道,E可以是负数的,所以IEEE另外规定,E是加上一个中间值存到内存中,E是8个bit位(取值范围0-255)的中间值是127,E是11个bit位(0-2047)的中间值是1123。5.0用科学计数表示的E是2,当要存到内存时 2+127=129,其实存的是129。这样即使E是-1,也可以用126代替,取的是后减掉就可以了。
M的存法:我们知道科学计数法的1<=M<2,M一定是表示成1.xxxx,为了让M的23个bit位都能用到,使精度更高,前面的1并不会存到内存中,而只存小数部分,后面有多余的位补零。
到这里,我们回过头来看看5.0的存法吧。
(-1)^0 * 1.010 * 2^2 S E M
01000000101000000000000000000000 在内存中是以十六进制显示的
0100 0000 1010 0000 0000 0000 0000 0000-- 0x40a00000
由于编译器是小端存储,所以反着读就是40a00000。
现在明白浮点数的存储方式后,这道题输出的怪异结果就迎刃而解了。
以整型存放9,在内存中存补码,正数原反补相同;
补码-00000000000000000000000000001001
而以浮点数取出来 0 00000000 00000000000000000001001
S=0, E=0-127=-127, M=1.00000000000000000001001
那为什么是0.000000而不是1.000000呢,这里涉及到E为全0、全1、非全零全1的三种情况。
1、E为全零,那说明在存到内存前,E的值是-127,加上127存到内存里才是全零,那么这个数一定是超小的无限接近于零,所以ICCC对这种情况就规定,M的1不用补了而是还原为0.xxxxxx的小数,E的值是1-127=-126即为真实值;所以就变成+ 0.00000000000000000001001 * 2^-126; 读取小数点后六位就是0.000000。
2、E为全1, 那么就是在存之前E就是128的值,是一个正负接近无穷的数 (+-)1.xxxxxx*2^128;
3、 E非全零全1是一般的正常情况,照算就可以了。
所以第二个printf打印出来的0.000000就解释完毕了。
以浮点数的形式存储9.0
(-1)^0* 1.0010 * 2 ^3-- 0 10000010 00100000000000000000000
以%d的形式打印01000001000100000000000000000000
以%lf的形式打印就是9.000000。
到这就是浮点数在内存中的存储啦!谢谢阅读。
以上是关于浮点数在内存中的存储的主要内容,如果未能解决你的问题,请参考以下文章