浮点数的存储与读取

目录
浮点数是什么?
为什么叫做浮点数?
浮点数存储规则
一道例题
浮点数是什么?
带有小数点的数就是浮点数,浮点数能表示小数和整数。浮点数家族有3种类型:float,double和long double类型。

例子:

浮点数3.14表示小数3.14
浮点数3.0表示整数3
为什么叫做浮点数?
在数学的科学计数法中,小数点在不同的位置也能表示同一个小数。
例子:

一个小数1234.5
用科学计数法可以等价于1.2345 x 103
也可以等价于12.345 x 102
观察这3个相同值中的小数点,像是在左右“浮动”,所以可以叫做浮点数。

浮点数存储规则
浮点数的规定标准是由IEEE(Institute of Electrical and Electronics Engineers)制定的IEEE 754标准。IEEE 754标准为浮点数提供了一套统一的表示方法和运算规则,使得不同计算机系统之间可以进行浮点数的交互和计算。

根据IEEE 754标准,任意的二进制浮点数由三个部分组成:符号位、指数位和尾数位。

符号位用于表示浮点数的正负;
指数位用于表示浮点数的数量级;
尾数位用于表示浮点数的精度。
用式子表示任意的二进制浮点数:(-1)S * M * 2E

S是符号位,如果S等于0就是正数,S等于1就是负数;
E是指数位,E是几,就是乘以2的几次方;
M是尾数位,可以理解为小数点后面的数。
计算机只需存储符号位S,指数位E和尾数位M,就能存储一个浮点数。

已知一个十进制的浮点数,如何获取S,E和M呢?下面通过一个例子说明:

设一个十进制的浮点数为V并且令V = 5.5(假设这个浮点数是5.5)
把5.5转换为二进制的浮点数101.1。具体的换算过程如下图:
(需要注意二进制浮点数的权重,小数点右边从2的-1次方开始再到2的-2次方,以此类推)

把二进制的浮点数101.1转换成二进制的科学计数法的形式:1.011 x 22;
最后在1.011 x 22的前面再补上符号位即可,因为5.5是正数,所以乘上-1的0次方:(-1)0 x1.011x22
此时就能在(-1)0 x1.011x22 式子中得知S是0,E是2,M是1.011

再举一个浮点数为9.0的例子:

IEEE 754规定,存储浮点数时,根据精度的不同有两种存储方式:

32个比特位的单精度浮点数,其中最高的1位是符号位S(红色区域),紧接着的8个比特位是指数位E(绿色区域),在指数位后面的23个比特位是尾数位M(紫色区域)。

64个比特位的双精度浮点数(双精度浮点数比单精度浮点数更精确),其中最高的1位是符号位S(红色区域),紧接着的11个比特位是指数位E(绿色区域),在指数位后面的52个比特位是尾数位M(紫色区域)。

在上面提到过,内存想要存储一个浮点数,只需存储符号位S,指数位E和尾数位M即可。
根据IEEE 754的规定,之前的例子中算出来的指数位E和尾数位M还不能直接存储到内存中,E和M还有额外的要求:

尾数位M:规定M的取值范围是 1<=M<2,所以存储M时,整数部分的1直接省略,这样做的目的是可以少存储一个比特位,读取的时候再将整数部分的1补上即可,这就是为什么M叫做尾数位,因为存储M时只存储小数部分。
指数位E:规定存储E时,首先E得是一个无符号整型(unsigned int 类型)。
在存储32个比特位的单精度浮点数中,E(8bit)的取值范围是0 ~ 255;
在存储64个比特位的双精度浮点数中,E(11bit)的取值范围是0 ~ 2047。
但因为实际上科学计数法中的E可以是负数(以0.5为例)

为了避免E存储的是负数,IEEE 754规定存储E的真实值时,需要先加上一个中间数再存储到内存中。
在存储32个比特位的单精度浮点数中,这个中间数是127;
在存储64个比特位的双精度浮点数中,这个中间数是1023。
只有概念不好理解,举个例子,还是以0.5为例:
0.5转换成二进制的科学计数法中,E的真实值是-1,
如果0.5是以32位浮点数存储,那么E的真实值-1先加上127得到126,再把126存储到内存中;

如果0.5是以64位浮点数存储,那么E的真实值-1先加上1023得到1022,再把1022存储到内存中。

最后以5.5为例,完整的演示5.5怎么存储到内存中:

在内存中取出一个浮点数时,指数位E分为3种情况:

E全为0,此时的浮点数一定是非常接近于0的数,直接表示0。
在32位上E的真实值是-126(1-127),在64位上E的真实值是-1022(1-1023),
M的整数部分不还原1,而是还原0,变成0.xxxx。
E全为1,如果M全为0,表示正无穷大或负无穷大。
一般情况:把指数E的存储值减去127(或1023)得到E的真实值,
M的整数部分还原1,变成1.xxxx,符号位直接取,
最后用(-1)S * M * 2E得到结果。
一道例题
问以下代码分别输出的是什么:

#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;
}
 

注释:

#include<stdio.h>
int main()
{
    int n = 9;//整型的空间大小是32位
    // 00000000 00000000 00000000 00001001

    float* pFloat = (float*)&n;
    printf("n的值为:%d\n", n); //以整数的形式打印变量n中的数据9

    //通过浮点型指针pFloat访问00000000 00000000 00000000 00001001的时候,
    //是以浮点数的角度看待00000000 00000000 00000000 00001001的,
    //所以00000000 00000000 00000000 00001001会被看做
    //    0 00000000 00000000000000000001001
    //    S E        M
    //    0 -126     0.00000000000000000001001
    // E在内存中是全0,所以E是-126
    printf("*pFloat的值为:%f\n", *pFloat);//%f小数点右边只打印6位
    *pFloat = 9.0;
    //1001.0 - 二进制的9.0
    //1.001 * 2^3 - 二进制的科学计数法
    //(-1)^0 * 1.001 * 2^3 
    //S = 0  E = 3  M = 1.001
    //0 10000010 00100000000000000000000
    //9.0是以浮点数的形式放进变量n,但是变量n以整数的形式打印时,
    // 会被认为01000001000100000000000000000000是补码,最高位是符号位,后面是数值位
    //0 1000001000100000000000000000000 
    printf("num的值为:%d\n", n);//1,091,567,616
    printf("*pFloat的值为:%f\n", *pFloat);//9.0
    //注意打印的数据和打印的类型需要匹配
    return 0;
}
完。
————————————————
版权声明:本文为CSDN博主「关关不烦恼」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_73276255/article/details/131724430

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值