当前位置:网站首页>浮点数在内存中的存储方式
浮点数在内存中的存储方式
2022-08-11 02:58:00 【南猿北者】
前言
我们知道了整数是以补码的形式在内存中存储的,那么对于浮点数来说是不是也是这样呢?
切入主题
首先我们来看一段代码:
int main()
{
int n = 9;
float *pFloat = (float *)&n;//将n那块空间以浮点数看待
printf("n的值为:%d\n",n);
printf("*pFloat的值为:%f\n",*pFloat);
*pFloat = 9.0;
printf("num的值为:%d\n",n);
printf("*pFloat的值为:%f\n",*pFloat);
return 0;
}
运行结果是多少呢?
看一下:
为什么是这样呢?
如果浮点数和整数的存储方式是一样的,那么我们看到的第一个printf和第二个printf应该是一样的;第三个printf和第四个printf也应该一样;
可是结果显然不是这样!!!
这从侧面似乎说明浮点数和整数的存储规则似乎不一样;
那我们就得来了解一下浮点数的存储规则;
浮点数存储规则
详细解读:
根据国际标准IEEE(电气和电子工程协会) 754,任意一个二进制浮点数V可以表示成下面的形式:
(-1)^S * M * 2^E
(-1)^S表示符号位,当S=0,V为正数;当S=1,V为负数。
M表示有效数字,大于等于1,小于2。
2^E表示指数位。
举例来说:
十进制的5.0,写成二进制是 101.0 ,相当于 1.01×2^2 。
那么,按照上面V的格式,可以得出S=0,M=1.01,E=2。
十进制的-5.0,写成二进制是 -101.0 ,相当于 -1.01×2^2 。那么,S=1,M=1.01,E=2。
IEEE 754规定:
对于32位的浮点数,最高的1位是符号位s,接着的8位是指数E,剩下的23位为有效数字M。
对于64位的浮点数,最高的1位是符号位S,接着的11位是指数E,剩下的52位为有效数字M。
其中呢对于E区没有符号位,全部都是数值位。也就是说在E区的是无符号整数,那么E区的最大值是255(或2047)
那么对于0.0111(二进制)=(-1)^0* 1.11 *2 ^(-2);
实际是怎么存的呢?
其中S=0;E=-2;M=1.11;
我们上面不是说了嘛E区只能存无符号整数,那么对于负数如果存进去,再取出来就是个无符号整数,就不是原来的负数次幂了,最终造成存进去不变,取出来倒变了,十分不符合逻辑!!!为了解决这个问题,我们最终存进去的是E+127(E+1023),**也就是E+最大值的一半!!**这才是E区真实存在的数据;那么根据上述提供的数据,E区存的就应该是-2+127=125
转为二进制就是:01111101,那么既然按照这样存储那我的E区存的负数和正数就有限了,也就最多存-127~128次幂的数据;现在搞定了E区,我们再来看看M区,M区存的是有效数据区,我们知道我们每个二进制浮点数都能写出1. 几的形式,这个1似乎一直存在,为了方便。我们干脆不把这个1存进去,我们直接存小数部分待我们需要取的时候在加上个1;这样不就为我们多提供了一个数据位来存储有效数据嘛,我们的精度不也就提高了一位嘛!!!于是呢对于上述
M区实际存的就是11000000000000000000000
对于S区与整数一样存的都是符号位,0表示正,1表示负;
那么实际0.0111(二进制)在内存中的存储形式就是
0011111011000000000000000000000
当然我们看到一个浮点数内部的二进制位我们也能逆出所对应的十进制小数;
取数据
当然对于我们取数据,我们需要特别注意
1、E区不全为0或E区不全为1的情况;
对于这种情况,我们直接按照上述存数据的过程逆推就行了
2、E区全为0;
对于E区全为0
也就意味着指数位-127;
我们取出数据就是
也就意味着这是一个:
3、E区全为1
E全为1是不是意味着E=128;
取数据取出来是不是就是正负1点几乘以2的128次方2的128次方是不是一个很大的数字,为了表示它的大或接近与无穷,我们把M区内的数据全部清0再取出来;
以上就是浮点数在内存中的存储规则;
我们就来解释一下开头代码的意思;
我们看第一句话,int n=9;
将一个整型存入int 里面去,那么n中存放的就应该是9的补码;
00000000000000000000000000001001
转为16进制就是 00 00 00 09;是不是呢我们看看内存:

我们在看这句话,pfloat表示一个指向浮点数的指针
站在pFloat的角度看n就是一个浮点数,站在&n的角度看n就是一个整型
那么自然的我们printf打印的结果就会不一样:
第一个printf是将n看成有符号int来打印,第二个printf将n看出float类型来打印;
对于:0 00000000 00000000000000000001001
站在有符号整数的方式取出来就是9;
站在float的角度取出来就是:E区全为0,是一个非常小的数据,无线接近于0
我们打印的时候默认打印小数点后6位自然我们看到第二个printf打印出来是0.000000;
我们再来看这样,我们将n当作一个float类型来存数据:
首先9.0转换为二进制就是:
1001.0
也就是(-1)^ 01.0012^3;
S=0;
E=3+127=130;
M=001;
则内存布局就是:
0 10000010 00100000000000000000000
转为16进制就是:
41 10 00 00
观察一下内存:
也就是说n现在内存中的序列已经被改了:
0 10000010 00100000000000000000000
站在int类型方式看就是:1091567616
站在浮点数的角度看:就是9.000000;
这样与我们的结果一一应正了;
边栏推荐
猜你喜欢

The problem that Merge will be lost again after code Revert has been solved

Idea (preferred) cherry-pick operation

【Unity入门计划】Unity2D动画(1)-动画系统的组成及功能的使用

Detailed explanation of new features of ES advanced array function syntax

解决vim与外界的复制粘贴(不用安装插件)

多线程之ThreadPoolExecutor

学军中学推理社2017届招新试题

最倒霉与最幸运

Summary of debugging skills
![[4G/5G/6G专题基础-154]: 5G无线准入控制RAC(Radio Admission Control)](/img/5d/5e6cb3e4199c678c06a3b8fab37aa0.png)
[4G/5G/6G专题基础-154]: 5G无线准入控制RAC(Radio Admission Control)
随机推荐
代码 Revert 后再次 Merge 会丢失的问题,已解决
四大组件---ContentResolver
[机缘参悟-66]:怎样才能让别人愿意帮你:利益共享法则、“大道”、“人性”
leetcode:358. K 距离间隔重排字符串
Idea (优选)cherry-pick操作
多线程之ThreadPoolExecutor
comp3331-9331-21t1-midterm复习
CSAPP Data Lab
qtcreator调试webkit
全局大喇叭--广播机制
【PHP】入门知识
重庆纸质发票再见!开住宿费电子发票即将全面取代酒店餐饮加油站发票
调试技巧总结
Mysq_Note4
comp3331-9331-22t1-midterm复习辅导-tutorial week 5
CC0 与商业 IP:哪种模式更适合 NFT?
A Practical Arrangement of Map GIS Development Matters (Part 1)
ARM development (4) How to read the chip manual for novice Xiaobai, bare metal driver development steps and pure assembly to achieve lighting, assembly combined with c lighting, c to achieve lighting
Detailed explanation of new features of ES advanced array function syntax
增加对 Textbundle 的支持