【C语言】浮点型在内存中的存储

文章目录

  • 例题引入
  • 剖析原因
    • 浮点型的二进制转换(M)
    • 正负号之分(S)
    • 科学记数法(E)
    • 关于 S E M 在内存中的存储
    • 存取浮点型时的情况讨论
  • 例题解析
    • 整形存储为浮点型并输出
    • 浮点型存储为整形并输出


在我的上一篇博客中详细写了整形在内存中的存储,以及大小端的介绍,有兴趣可以看一下,本篇文章重点讲讲浮点型在内存中的存储。


例题引入

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

大家可以先猜测一下上述例题的输出结果可能是什么,如果不是很熟练或者对float类型在内存中存储的方式不同的话,很有可能会认为浮点数和整型在内存中的存储是一样的,然而不对,正确的输出结果如下

n的值为:9
*pFloat的值为:0.000000
num的值为:1091567616
*pFloat的值为:9.000000


剖析原因

那接下来就讲讲这其中的原因

在上一篇博客中我提到,所有整形都是以二进制补码的形式存储在内存中的,而浮点型虽然也是以二进制存储在内存中,但是它遵循着国际标准IEEE(电气和电子工程协会)754

V = ( − 1 ) S ∗ M ∗ 2 E ∙ ( − 1 ) S 表示符号位,当S=0, V为正数;当S=1, V为负数 ∙ M 表示有效数字,M是大于等于1,小于2的 ∙ 2 E 表示指数位 \begin{aligned} &V=(-1)^S*M*2^E \\ &\bullet(-1)^S\text{ 表示符号位,当S=0, V为正数;当S=1, V为负数} \\ &\bullet\text{ M 表示有效数字,M是大于等于1,小于2的} \\ &\bullet2^E\text{ 表示指数位} \end{aligned} V=(1)SM2E(1)S 表示符号位,S=0, V为正数;S=1, V为负数 M 表示有效数字,M是大于等于1,小于22E 表示指数位

大家第一次见应该都没法一下理解,现在我以 5.5 这个浮点数为例,通过画图解释一下,IEEE中的 M,S,E

浮点型的二进制转换(M)

首先我们要将浮点数在转换为二进制数
下图就是二进制的转换方法,首先5.5的二进制数是101.1

可以理解二进制的转换规则为凑,相当于在小数点两边一直是2的n次方,但是左边的n是从0开始依次递增,右边的n是从-1开始递减,当1代表有,0代表没有,左右两边独立计算。

以左边为例 1 * 22 + 0 * 21 +1 * 20 = 5,这样左边的5的二进制就凑出来了,相同的,右边 1 * 2-1 = 0.5,右边的二进制也就出来了,放在一起就是浮点数的二进制了。

在这里插入图片描述
而5.5转换成二进制后的101.1对应的就是IEEE中的M


正负号之分(S)

M出来了,先理解一下好理解的S

举例来说:

十进制的5.0,写成二进制是 101.0,相当于 1.01x2^2。
那么,按照上面V的格式,可以得出S=0,M=1.01,E=2.

十进制的-5.0,写成二进制是-101.0,相当于-1.01x2^2。
那么,S=1,M=1.01,E=2.

意思就是在IEEE中V最前面都是以 (-1)^S 的形式出现,如果这个浮点数是整数,S为0,反之为1


科学记数法(E)

小学我们就学过十进制的科学计数法,比如10000的科学计数法就是1*104 这里的E也是一样,IEEE把M的值设定为在1和2之间,所以我们需要把M的小数点放在第一个1的后面,而往前往后多少位就决定了E的正负多少数

比如
1000.1的科学计数法就是1.0001 * 23
0.000101的科学计数法就是1.01 * 2-4

还是比较好理解的,而之所以的2的次方是因为当前是二进制
但先注意下这里的E并不是最后储存的E,等S,E,M的内存存储的形式了解后在解释


关于 S E M 在内存中的存储

对于32位的浮点数,最高的1位存储符号位S,接着的8位存储指数E,剩下的23位存储有效数字M
对于64位的浮点数,最⾼的1位存储符号位S,接着的11位存储指数E,剩下的52位存储有效数字M

如下图
在这里插入图片描述
在这里插入图片描述
注意点1 (M的保存形式)
前面说过, 1≤M<2 ,也就是说,M可以写成1.xxxxxx 的形式,其中 xxxxxx 表示小数部分。IEEE 754 规定,在计算机内部保存M时,默认这个数的第一位总是1,因此可以被舍去,只保存后面的xxxxxx部分。比如保存1.01的时候,只保存01,等到读取的时候,再把第一位的1加上去。这样做的目的,是节省1位有效数字。以32位浮点数为例,留给M只有23位,将第一位的1舍去以后,等于可以保存24位有效数字。

注意点2 (E的保存形式)
首先,E为一个无符号整数(unsignedint)

这意味着,如果E为8位,它的取值范围为0 ~ 255;如果E为11位,它的取值范围为0 ~ 2047。但是,我们知道,科学计数法中的E是可以出现负数的,所以IEEE754规定,存入内存时E的真实值必须再加上一个中间数,对于8位的E,这个中间数是127;对于11位的E,这个中间数是1023。比如,2^10的E是10,所以保存成32位浮点数时,必须保存成10+127=137,即10001001。


存取浮点型时的情况讨论

指数E从内存中取出还可以再分成三种情况:

情况1:E不全为0或不全为1

这时,浮点数就采用下面的规则表示,即指数E的计算值减去127(或1023),得到真实值,再将有效数字M前加上第一位的1。
比如:0.5的二进制形式为0.1,由于规定正数部分必须为1,即将小数点右移1位,则为1.0*2^(-1),其阶码为-1+127(中间值)=126,表示为 0111 1110,而尾数1.0去掉整数部分为0,补齐0到23位00000000000000000000000,则其二进制表示形式为:

0 01111110 00000000000000000000000

情况2:E全为0

这时,浮点数的指数E等于1-127(或者1-1023)即为真实值,有效数字M不再加上第一位的1,而是还京为0.xxxxxx的小数。这样做是为了表示士0以及接近于0的很小的数字

0 00000000 00100000000000000000000

情况3:E全为1

这时,如果有效数字M全为0,表示士无穷大(正负取决于符号位s)。

0 11111111 00010000000000000000000


例题解析

整形存储为浮点型并输出

我们回到最开始引入的例题
先看上面这一部分

#include <stdio.h>
int main() {int n = 9;float* pFloat = (float*)&n;printf("n的值为:%d\n", n);printf("*pFloat的值为:%f\n", *pFloat);
}

首先 n 作为整形在内存中是以二进制补码的形式存储,正整数的补码是其本身

9的二进制为(X86环境下)
0000 0000 0000 0000 0000 0000 0000 1001

类型转换时,它不会先把原来的9以IEEE规则分解然后存储,而是直接以9的二进制形式进行存储,也就是说会变成下图情况
在这里插入图片描述
因为前面说到当E全为0时,E的真实值为1-127,也就是-126
所以最后存储的V为

V=(-1)0 × 0.00000000000000000001001×2(-126)=1.001×2(-146)

显然,V是一个很小的接近于0的正数,所以用十进制小数表示就是0.000000。
所以上面部分的代码的结果就是

n的值为:9
*pFloat的值为:0.000000

浮点型存储为整形并输出

再来看下半部分的代码

#include <stdio.h>
int main() {*pFloat = 9.0;printf("num的值为:%d\n", n);printf("*pFloat的值为:%f\n", *pFloat);return 0;
}

我们先把9.0在内存中的存储形式表达出来

首先,9.0是正数,所以S - 0

然后,9.0的二进制为1001.0,

所以 V=(-1)0 × 1.001×2(3)

最后得出

S - 0
E - 3+127 = 130 - 10000010
M - 去掉首位1 - 得001 - 补齐23位 - 001 0000 0000 0000 0000 0000

最后在内存中得存储
0 10000010 001 0000 0000 0000 0000 0000

通过计算器我们可以知道上面这个二进制数直接转换为十进制数为
在这里插入图片描述
这样我们就得出了最后的结果

num的值为:1091567616
*pFloat的值为:9.000000


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/750233.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

接雨水-热题 100?-Lua 中文代码解题第4题

接雨水-热题 100&#xff1f;-Lua 中文代码解题第4题 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图&#xff0c;计算按此排列的柱子&#xff0c;下雨之后能接多少雨水。 示例 1&#xff1a; 输入&#xff1a;height [0,1,0,2,1,0,1,3,2,1,2,1] 输出&#xff1a;6 解释…

算法——前缀和之除自身以外数组的乘积、和为K的子数组、和可被K整除的子数组、连续数组、矩阵区域和

这几道题对于我们前面讲过的一维、二维前缀和进行了运用,包含了面对特殊情况的反操作 目录 4.除自身以外数组的乘积 4.1解析 4.2题解 5.和为K的子数组 5.1解析 5.2题解 6.和可被K整除的子数组 6.1解析 6.2题解 7.连续数组 7.1题解 7.2题解 8.矩阵区域和 8.1解析 …

GET和POST方法的区别

GET和POST的区别 在我们开发项目的时候常常会在Controller层使用到POST方法或者GET方法&#xff0c;犹豫到底将接口定义为GET方法还是POST方法&#xff1f;那这两者之间有什么区别呢&#xff1f; 看一下官方定义&#xff1a; GET 和 POST 是 HTTP 协议中最常用的两种请求方法…

爬虫学习 Scrapy中间件代理UA随机selenium使用

目录 中间件UA、代理处理---process_requestUA随机 代理处理seleniumscrapy 中间件 控制台操作 (百度只起个名 scrapy startproject mid scrapy genspider baidu baidu.com setting.py内 ROBOTSTXT_OBEY FalseLOG_LEVEL "WARNING"运行 scrapy crawl baidu middle…

ArcGIS分享图层数据的最佳方法

在工作中&#xff0c;经常需要将图层数据分享给其他人。 如下图所示&#xff0c;需要分享的是【CJDCQ】和【GHDLTB】&#xff0c;图层带有符号系统&#xff1a; 一、分享gdb数据库及lyr文件 分享数据自然要找到源数据&#xff1a; 但是&#xff0c;gdb数据是不带符号系统的&a…

微信小程序开发系列(三十四)·自定义组件的创建、注册以及使用(数据和方法事件的使用)

目录 1. 分类和简介 2. 公共组件 2.1 创建 2.2 注册 2.3 使用 3. 页面组件 3.1 创建 3.2 注册 3.3 使用 4. 组件的数据和方法的使用 4.1 组件数据的修改 4.2 方法事件的使用 1. 分类和简介 小程序目前已经支持组件化开发&#xff0c;可以将页面中的功能…

springboot基于spring boot的在线答题微信小程序

摘 要 在线答题微信小程序是考试中重要的一环&#xff0c;在线答题是学生获取任务信息的主要渠道。为了方便学生能够在网站上查看任务信息、考试&#xff0c;于是开发了基于 springboot框架设计与实现了一款简洁、轻便的在线答题微信小程序。本微信小程序解决了在线答题事务中的…

2.3 HTML5新增的常用标签

2.3.1 HTML5新增文档结构标签 在HTML5版本之前通常直接使用<div>标签进行网页整体布局&#xff0c;常见布局包括页眉、页脚、导航菜单和正文部分。为了区分文档结构中不同的<div>内容&#xff0c;一般会为其配上不同的id名称。例如&#xff1a; <div id"h…

FFmpeg转码参数说明及视频转码示例

-b : 设置音频或者视频的转码码率 -b:v 只设置视频码率 -b:a 只设置音频码率 -ab: 只设置音频码率, 默认码率大小为: 128k bit/s -g: 设置视频GOP大小,表示I帧之间的间隔,默认为12 -ar: 设置音频采样率,默认0 -ac: 设置音频通道数量 默认0 -bf: 设置连…

CTFHUB-web-信息泄漏

题目所在位置&#xff1a;技能树->web->信息泄漏 目录遍历 打开题目&#xff0c;我们进入的是这个页面 翻译过来就是 得到的信息就是&#xff1a;flag要在这些目录里面寻找&#xff0c;我们直接一个一个点开查看就行 发现得到一个flag.txt&#xff0c;点击打开得到flag …

鸿蒙Harmony应用开发—ArkTS声明式开发(容器组件:UIExtensionComponent (系统接口))

UIExtensionComponent用于支持在本页面内嵌入其他应用提供的UI。展示的内容在另外一个进程中运行&#xff0c;本应用并不参与其中的布局和渲染。 通常用于有进程隔离诉求的模块化开发场景。 说明&#xff1a; 该组件从API Version 10开始支持。后续版本如有新增内容&#xff0…

【Java】List, Set, Queue, Map 区别?

目录 List, Set, Queue, Map 区别&#xff1f; Collection和Collections List ArrayList 和 Array区别&#xff1f; ArrayList与LinkedList区别? ArrayList 能添加null吗&#xff1f; ArrayList 插入和删除时间复杂度&#xff1f; LinkedList 插入和删除时间复杂度&…

计算机网络-数据链路层

一、认识以太网 "以太网" 不是⼀种具体的网络&#xff0c;而是一种技术标准; 既包含了数据链路层的内容, 也包含了⼀些物理 层的内容。 例如&#xff1a;规定了网络拓扑结构&#xff0c;访问控制方式&#xff0c;传输速率等; 例如&#xff1a;以太网中的网线必须使用…

vxe-table表格组件的使用已经query函数扩展

最近新项目使用vue3typescript开发后台管理系统&#xff0c;基本上展示内容一致表格的方式展示&#xff0c;所以使用vxe-table组件来开发&#xff0c;主要是为了方便使用工具栏&#xff0c;以及其他表格操作。 vxe-table 开发文档&#xff1a;https://vxetable.cn/#/table/sta…

免费开源多层级多标签文本分类|文本分类接口|文本自动分类

一、开源项目介绍 一款多模态AI能力引擎&#xff0c;专注于提供自然语言处理&#xff08;NLP&#xff09;、情感分析、实体识别、图像识别与分类、OCR识别和语音识别等接口服务。该平台功能强大&#xff0c;支持本地化部署&#xff0c;并鼓励用户体验和开发者共同完善&#xf…

火车订票管理系统|基于springboot框架+ Mysql+Java+B/S结构的火车订票管理系统设计与实现(可运行源码+数据库+设计文档)

推荐阅读100套最新项目 最新ssmjava项目文档视频演示可运行源码分享 最新jspjava项目文档视频演示可运行源码分享 最新Spring Boot项目文档视频演示可运行源码分享 目录 前台功能效果图 管理员功能登录前台功能效果图 用户功能模块 系统功能设计 数据库E-R图设计 lunwen…

代码随想录算法训练营第二十五天 | 216. 组合总和 III、17. 电话号码的字母组合

代码随想录算法训练营第二十五天 | 216. 组合总和 III、17. 电话号码的字母组合 216. 组合总和 III题目解法 17. 电话号码的字母组合题目解法 感悟 216. 组合总和 III 题目 解法 修改上一天组合的代码 class Solution { public:vector<vector<int>> result;vect…

双向SSM: Vision Mamba Encoder

文章目录 Vision Mamba Encoder初始化输入映射序列变换参数映射BC参数映射delta参数映射 SSM参数初始化A , D矩阵初始化delta参数初始化 双向SSM初始化参数初始化 前向输入映射fast_pathuse_fast_pathno use_fast_path 双向SSMv1前向后向 v2前向后向 Vision Mamba Encoder Vis…

数据结构的概念大合集04(队列)

概念大合集04 1、队列1.1 队列的定义1.2队列的顺序存储1.2.1 顺序队1.2.2 顺序队的基本运算的基本思想1.2.3 顺序队的4要素的基本思想 1.3 环形队列1.3.1 环形队列的定义1.3.1 环形队列的实现 1.4 队列的链式存储1.4.1 链队1.4.2 链队的实现方式1.4.3 链队的4要素的基本思想 1.…

C语言之快速排序

目录 一 简介 二 代码实现 快速排序基本原理&#xff1a; C语言实现快速排序的核心函数&#xff1a; 三 时空复杂度 A.时间复杂度 B.空间复杂度 C.总结&#xff1a; 一 简介 快速排序是一种高效的、基于分治策略的比较排序算法&#xff0c;由英国计算机科学家C.A.R. H…