首先我们先来引入一个例子:
int main()
{
int n = 9;
float* pFloat = (float*)&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;
}
这串代码的输出结果是:
这个结果可能和我们预想的不同,那么为什么会产生这种差异呢?
这里必须得了解浮点数在内存中的存储。
浮点数在内存中的存储:
根据国际标准IEEE 754,任意⼀个⼆进制浮点数V可以表⽰成下⾯的形式:
V = (−1) ^S * M * 2^E
注意:
^表示指数
*表示乘号
符号含义:
-1^S决定这个浮点数的正负,S为0代表正,S为1代表负
M表示这个数的有效位数,1<=M<2
2^E表示指数位
这里举个简单的栗子:
十进制的5.0,写成⼆进制是 101.0 ,相当于 1.01×2^2 。
那么这S,M,E该如何确定呢?
IEEE 754规定:
对于32位的浮点数,最⾼的1位存储符号位S,接着的8位存储指数E,剩下的23位存储有效数字M
对于64位的浮点数,最⾼的1位存储符号位S,接着的11位存储指数E,剩下的52位存储有效数字M
图为32位:
注意:S,M,E的存储顺序
浮点数的存过程:
对于M:
由于M是大于等于1小于2的,所以计算机在存储时默认舍去1,只保存后面的部分,当读取时再加上1,这样就可以省下来一位的空间,E就可以多保留一位
对于E:
E为⼀个无符号整数。但是,科学计数法中的E是可以出现负数的,所以IEEE 754规定,存入内存时E的真实值必须再加上一个中间数,对于8位的E,这个中间数是127;对于11位的E,这个中间数是1023。
举个栗子:2^10的E是10,所以保存成32位浮点数时,必须保存成10+127=137,即10001001。
浮点数取的过程:
浮点数取的情况有点复杂,分为三种情况:
情况一:E不全为0或者1—>即为正常数字
这种就是存储时的反操作,将当时加的127(或1023)减去
,得到真实值,再将有效数字M前加上第⼀位的1。
情况二:E全为0
这时,浮点数的指数E等于1-127(或者1-1023)即为真实值,有效数字M不再加上第⼀位的1,而是还原为0.xxxxxx的小数。
这样做是为了表示±0,以及接近于0的很小的数字。
可以这样理解:2^(1-127)次方本身就是一个很小的数字了,前面所乘的对这个值的改变不大,仍然是趋近于0的
情况三:E全为1
这时,如果有效数字M全为0,表示±无穷大(正负取决于符号位s)
可以这样理解:11111111这个指数本身就很大了,减去127后,2^(255-127)仍然是一个很大的数字,趋向于无穷大
了解了以上知识,我们来解决刚开始的栗子:
int n=9时
定义成int类型的,且大于0,他的原码,反码,补码相同
整数在计算机中以补码的形式存储:
补码为:00000000 00000000 00000000 00001001
以%d打印时:以上的补码被按照整数的形式来打印
以%f打印时,这段补码被当作浮点数看待
0 00000000 00000000000000000001001
分别为S,E,M
由于E为0,由情况二知为0,再由%f确定有效位数
所以最终输出为0.000000
*pFloat=9时:
由于其是float类型的,因此9.0存储时按照浮点数的存储方式
二进制:1001.0—>(-1)^S1.001 * 2^3
则:
在内存中的存储为:
0 10000010 00100000000000000000000
依次为S E M
以%d打印:从整数的形式看就是
01000001000100000000000000000000表示的十进制数,转化后即为打印的值
以%f打印,就是将存的操作倒着来,所以打印的值仍为9,小数点后保留的位数由%f决定
以上就是本次所有内容,希望对大家有所帮助,谢谢观看。