首先,看一段代码:
int main()
{
double d = 3.14;
float f = d;
if ((float)d == f) {
printf("hello");
}
if (d != f) {
printf(" world!");
}
}
这个程序的输出结果是: hello world!
然后你有没有产生了好奇???这是为啥,下面我就来具体讲一讲float在内存中的存储方式!!!
目前所有的计算机都支持一个被称为IEEE浮点的标准.题外话:电器气和电子工程师协会(IEEE
)是一个包括所有电子和计算机技术的专业团体.它出版刊物,举办会议,并且建立委员会来定义标准,内容涉及从电力传输到软件工程.另一个IEEE标准的例子是无线网络的802.11标准.
IEEE浮点标准用 的形式来表示一个数:
-
符号(sign): s决定这数是负数还是正数(s = 0),而对于数值0的符号位解释作为特殊情况处理.
-
尾数(significand) : M是一个二进制小数,它的范围是1~2- ,或者是0~1- .
-
阶码(exponent): E的作用是对浮点数加权,这个权重是二的E次幂(可能是负数).将浮点数的位划分为是三个字段,分别对这些值进行编码:
-
一个单独的符号位s直接符号编码s.
-
k位的阶码字段 编码E
-
n位小数字段 编码尾数M,但是编码出来的值也依赖于阶码字段是否等于0.
在单精度浮点格式(C语言中的float)中,s,exp和frac字段分别为1位,k=8位和n=23位,得到一个32位的表示。
说了这么多,你可能还是看得云里雾里,没关系!!!接下来,我们还是通过代码来看一看:
int main()
{
float f = 1;e
printf("%x\n", *(int *)&f);
}
输出结果: 3f800000
上图为1作为float类型的内存中的表示,所以它到底是怎么算的?
- 其实这个是 ,其中 是无符号数,其位表示为 ,而Bias是一个等于 ,单精度是127的偏置值。由此产生指数的取值范围,对于单精度是-126~127.
- 小数字段 被解释为描述小数值f, 其中 , 器二进制表示为 ,也就是二进制小数点在最高有效位的左边。尾数定义 . 有时,这种方式也叫隐含的以1开头表示,因为我们可以把 看做一个二进制表达式为 的数字。既然我们总是能够调整阶码 ,使得尾数 在范围 之中(假设没有溢出),那么这种表示方法是一种轻松获得一个格外精度位的技巧。既然第一位总是等于1,那么我们就不需要显示地表示它。
说了这么多, , 现在这个公式能看懂了吗???
- 在 范围内,小数部分由frac部分表示
- 在 范围内, 小数部分由exp部分表示
能看懂的话这部分基本就掌握了,如果还看不懂,就在自己的电脑上多输出一些float的值,在画一画,就会明白了。
留一个问题,float类型真的不能精确表示所有值,比如0.50(
)
是不是觉得关于浮点数的问题都解决了,emmmm……
别急,又一朵乌云有轮罩即将来袭,哈哈哈哈,你觉得下面这个程序的输出结果将会是什么???
int main()
{
float f = 1;
printf("%d\n", f);
}
你觉得会是什么呢???哈哈哈哈,随机值
之所以会出现随机值,是因为计算机是有专门的浮点数寄存器的,但是printf中的参数%d计算机会默认的从一些通用寄存器中拿值,而不会从浮点寄存器中拿,所以会导致随机值现象的出现。
基本说完了float类型的存储,但是也并没有介绍关于对于不能精确表示的浮点的舍入,主要这部分我也不是很了解,之后如果了解了,再来补充吧。
下图是double双精度类型的内存存储示意图
下面回到这篇文章一开始的那个程序吧,能理解了吗
假如我将double 的= 0.5
,你觉得程序的输出结果又是什么???
输出结果是hello
最后留一个问题,你觉得在浮点数0能精确表示吗???
今天看到一个小伙伴写的float存储的文章,恍然大悟,我写的这篇文章更倾向于从概念方面解释,但小伙伴的文章确实从为什么方面解释了,写的很好,大家可以看看。https://blog.csdn.net/weixin_43812622/article/details/103113718