float 类型的存储

首先,看一段代码:

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浮点标准用 V = ( − 1 ) S × 2 E × M V = (-1)^S \times 2^E\times M V=(1)S×2E×M的形式来表示一个数:

  • 符号(sign): s决定这数是负数还是正数(s = 0),而对于数值0的符号位解释作为特殊情况处理.

  • 尾数(significand) : M是一个二进制小数,它的范围是1~2- ϵ \epsilon ϵ ,或者是0~1- ϵ \epsilon ϵ.

  • 阶码(exponent): E的作用是对浮点数加权,这个权重是二的E次幂(可能是负数).将浮点数的位划分为是三个字段,分别对这些值进行编码:

  • 一个单独的符号位s直接符号编码s.

  • k位的阶码字段 e x p = e k − 1 … … e 1 e 0 exp = e_{k-1} …… e_1e_0 exp=ek1e1e0编码E

  • n位小数字段 f r a c = f n − 1 … … f 1 f 0 frac = f_{n-1} …… f_1f_0 frac=fn1f1f0编码尾数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类型的内存中的表示,所以它到底是怎么算的?
V = ( − 1 ) S × 2 E × M V = (-1)^S \times 2^E\times M V=(1)S×2E×M

  • 其实这个是 E = e − B i a s E = e-Bias E=eBias,其中 e e e是无符号数,其位表示为 e k − 1 … … e 1 e 0 e_{k-1}……e_1e_0 ek1e1e0,而Bias是一个等于 2 k − 1 − 1 2_{k-1}-1 2k11,单精度是127的偏置值。由此产生指数的取值范围,对于单精度是-126~127.
  • 小数字段 f r a c frac frac被解释为描述小数值f, 其中 0 ≤ f < 1 0 \le f < 1 0f<1, 器二进制表示为 0. f n − 1 … … f 1 f 0 0.f_{n-1}……f_1f_0 0.fn1f1f0,也就是二进制小数点在最高有效位的左边。尾数定义 M = 1 + f M = 1 + f M=1+f. 有时,这种方式也叫隐含的以1开头表示,因为我们可以把 M M M看做一个二进制表达式为 1. f n − 1 f n − 2 … … f 0 1.f_{n-1}f_{n-2}……f_0 1.fn1fn2f0的数字。既然我们总是能够调整阶码 E E E,使得尾数 M M M在范围 1 ≤ M < 2 1\le M < 2 1M<2之中(假设没有溢出),那么这种表示方法是一种轻松获得一个格外精度位的技巧。既然第一位总是等于1,那么我们就不需要显示地表示它。

说了这么多, V = ( − 1 ) S × 2 e − 127 × 1. f V = (-1)^S \times 2^{e-127}\times 1.f V=(1)S×2e127×1.f, 现在这个公式能看懂了吗???

  • ∣ f ∣ ≥ 1 \vert f\vert \ge 1 f1范围内,小数部分由frac部分表示
  • ∣ f ∣ < 1 \vert f\vert < 1 f<1范围内, 小数部分由exp部分表示

能看懂的话这部分基本就掌握了,如果还看不懂,就在自己的电脑上多输出一些float的值,在画一画,就会明白了。
留一个问题,float类型真的不能精确表示所有值,比如0.50( 1 2 \frac 12 21)

是不是觉得关于浮点数的问题都解决了,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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值