重新研究一下浮点数的内部储存机制
写这篇文章的原因有很多,学校大一上学期开展的大学计算机没有把浮点数学懂,或者说也没想着讲明白,这篇文章写在考完C语言期末考试的第六天,希望通过利用浙大C语言mooc
建议的一个转二进制网站和联合这个好工具来重新理解浮点数的储存方式
---2022.6.29
浮点数组成
由国际标准IEEE754规定,任意一个二进制浮点数V都可以表示成下列形式:V=(−1)Sign∗M∗2Exponent
float |
Sign(符号位) |
Exponent(指数位) |
Mantissa(尾数位) |
32bit |
1 |
8 |
23 |
double |
Sign(符号位) |
Exponent(指数位) |
Mantissa(尾数位) |
64bit |
1 |
11 |
52 |
例子分析
代码
typedef union{
float value;
char mem[sizeof(float)];
}MEMF;
typedef union{
double value;
char mem[sizeof(double)];
}MEMD;
直接定义两个联合体,同时以value
的类型存值,同时可以用mem
去输出每一字节的储存情况,从而达到研究浮点数储存方式的效果;我们下面首先研究单精度浮点数float
的储存方式;
void demo1() {
MEMF a;
a.value=0.5;
printf("%.2f|", a.value);
for (unsigned i = 0; i < sizeof(float); i++) {
printf("%02hhX ", a.mem[i]);
}
}
%02hhX
出现很多次了,是指以一个字节的形式的十六进制输出,2位,少的补零;可以参考%02hhX
- 上面程序就是从左往右输出内存空间二进制分布的字节形式,输出如下:
0.50|00 00 00 3F
分析
- 由机器的小端存储方式(字节序与内存序相反)我们知道实际的应该是
3F 00 00 00
,在网站中验证如下图:
-
0011 1111
=3F
,0000 0000
=00
,左边为二进制,右边为十六进制
-
Sign=0
说明是正数,Exponent=01111110
第一个0是符号的意思,0为负数,1为正数,1111110
不难发现表达的是1的意思(指数位采用的类似反码的表示方法,但最左边第一位不同),则表示成十进制Exponent=01111110=-1
;那么,如果我们把最令Exponent=01111111=0
,其它位不变,则表示什么呢——就是1,因为我们的尾数会把1省略掉,即M默认是1;
归纳(猜测着总结):
0.5=(−1)Sign∗M∗2Exponent=(−1)0∗1∗2−1=(−1)0↑01bit2−1↑011111108bits0000000000000000000000023bits(1)(2)(3)
演绎(尝试着解释):
0.25=(−1)Sign∗M∗2Exponent=(−1)0∗1∗2−2=(−1)0↑01bit2−2↑011111018bits0000000000000000000000023bits(4)(5)(6)
再试了几个例子,觉得应该改一下猜测
我们的Exponent
好像并不是用反码的形式,发现应该是这样的;
将Exponent
左边第一位取反,再对Exponent
取补码即为所得结果:
01111110⇒11111110⇒100000000−11111110⇒00000010=2⇒21−2=0.5
即对于float来说21+Exponent−28−1=2Exponent−127才是指数位真正的因子
几行例子快速明白
0.5=(−1)Sign0×2Exponent01111110−01111111×(1×20+Mantissa000000000000000000000000×2−1+0×2−2+⋯)=1×2−1×1=0.5(7)(8)(9)
−123.5=(−1)Sign1×2Exponent10000101−01111111×(1×20+Mantissa111011100000000000000001×2−1+1×2−2+⋯)=(−1)×200000110×(1×20+1×2−1+1×2−2+1×2−3+0×2−4+1×2−5+1×2−6+1×2−7)=(−1)×26×(1+2−1+2−2+2−3+2−5+2−6+2−7)=(−1)×(26+25+24+23+21+20+2−1)=−123.5(10)(11)(12)(13)(14)
🌹desmos
等很多数学计算网站都是支持latex
公式直接计算的,上面这个式子的计算就是直接复制最后一行的结果;
总结
单精度浮点数的公式是
F=(−1)Sign×2Exponent−27+1×(1.Mantissa)2其中上面三个参数都是直接替换成二进制然后转化成十进制运算即可,其中(1.Mantissa)2=1+(0.Mantissa)2我们总是会保留个1出来所以Mantissa也被翻译成尾数部分,比如(0.1)2=0.5
其实研究完单精度我们的双精度也很容易理解现在了,就是Exponent
部分变成十一位,相应的公式也应该是
D=(−1)Sign×2Exponent−210+1×(1.Mantissa)
工具:
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通