文章目录
- 前言
- 一、浮点数在内存中的存储
- 练习引入
- 浮点数的存储
- 浮点数存的过程
- 二、浮点数取的过程
- E不全为0或不全为1
- E全为0
- E全为1
- 三、再回顾练习
- 总结
前言
哎,之前写了一篇,可是中途退出没保存,只能再写一遍了~
浮点数在内存中的存储跟整型还是有很大区别的
正文开始!
一、浮点数在内存中的存储
常见的浮点数有:3.14159、1E10,浮点数家族包括float、double、long double等类型
浮点数表示的范围:float.h 中定义
练习引入
#include <stdio.h>
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 = (-1)S * M * 2E
- (-1)S 表示符号位,当 S = 0 时,V 为正数;当 S = 1 时,V为负数
- M表示有效数字,M是大于等一1,小于2的
- 2E 表示指数位
举个例子:
十进制的5.0,写成二进制是101.0,相当于1.01 * 22 ,那么按照 V 的形式,可以得出S = 0,M = 1.01,E = 2
IEEE 754规定:
对于32位的浮点数,最高的1位存储符号位是S,接着的是8位存储指数E,剩下的23位存储有效数字M
对于64位的浮点数,最高的1位存储符号位是 S,接着的是11位存储指数E,剩下的52位存储有效数字M
还有你想一下3.14,二进制是11.几,这个“几”到底是多少?,运用乘2看个位的方法可以发现,很难取尽,超过了float的M能存的二进制位,所以,有相当一部分浮点数,其实是有精度丢失的
暂时先不管这个,不如我们先再来看看存储:
浮点数存的过程
IEEE 754对有效数字M和指数E,还有一些特别规定
前面说过,1 <= M < 2,M可以写成1.xxxxxx形式,其中xxxxxx表示小数部分
IEEE 754规定,在计算机内部保存M的时候,因为百分百可以确保这个数的第一位总是1,所以可以被舍去,只保存后面的xxxxx部分,比如保存1.01的时候,只保存01,等到读取的时候,再把第一位的1加上去。这样做的目的是节省1位有效数字。以32位浮点数为例,留给M只有23位,将第一位的1舍去后,等于可以保存24位有效数字。
至于指数E,情况就比较复杂
首先,E是一个无符号整数
这意味着,如果E为8位,它的取值范围为 0 ~ 255,如果E为11位,它的取值范围为 0 ~ 2047 但是,我们知道,科学计数法的E是可以出现负数的,所以IEEE 754规定,存入内存时的真实值必须再加上一个中间数,对于8位的E,这个中间数是127;对于11位的E,这个中间数是1023。比如,210 的E是10,所以保存成32位浮点数时,必须保存成10 + 127 = 137,即10001001
二、浮点数取的过程
这个过程比较重要,于是我们单开来讲
指数E从内存中取出还可以再分成三种情况
E不全为0或不全为1
这时,浮点数就采用下面的规则表示,即指数的计算值减去127(或1023),得到真实值,再将有效数字M前加上第一位的1。
比如:0.5 的二进制表示为 0.1,由于规定正数部分必须为 1,即将小数点右移 1 位,则为 1.0*2^(-1),其余部分为 -1 + 127(中间值)= 126,表示为 01111110,而尾数 1.0 去掉整数部分为 0,补齐 0 到 23 位 00000000000000000000000
则其二进制表示形式为:
0 01111110 00000000000000000000000
E全为0
说明真实E为 -127 ,你可以想象(-1)S * 1.几 * 2-127 ,相当于是无穷接近于0,这时候就不需要按照正常形式取出了,而是M不再加上第一位的1,而是还原为0.xxxxx的小数,这样做是为了表示±0,以及接近于0的很小的数字
0 00000000 00100000000000000000000
E全为1
说明真实E为128,这时候,如果有效数字M全为0,表示±无穷大
0 11111111 0001000000000000000000
当然,E为全0或者全1仅作了解即可
三、再回顾练习
我们再看前文的代码
9是正数,补码很好求解,我们如果以%f打印,相当于把9的补码当陈了浮点数,E全为0,那么结果就为0
而下半部分,把9.0f按照浮点数的形式存储,9就是1001.0,科学计数法就是1.001 * 23 ,这时候在内存中的存储就是0 10000010 00100000000000000000000,以%d打印出来就是1 091 567 616
另外,我们思考,两个浮点数比较大小的时候,直接使用 == 比较可能存在问题
所以,这时候确保自己的精度位,保证精度即可
总结
但是我还是再写了一遍!
还是挺满意这篇的,比较精炼