浮点数在内存中的存储以及用指针改变内存与强制转换的区别

news2025/1/13 8:10:17

文章目录

  • 浮点型在内存中的存储
    • 引例
    • 浮点数的表示形式
    • 浮点数的存储
          • E不全为零且E不全为1
          • E全为0
          • E全为1
      • Eg
    • 总结
      • 用指针改变内存和强制转换的区别

浮点型在内存中的存储

引例

  • 我们先来看下面一段代码

    #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("n的值为:%d\n", n);
    	printf("*pFloat的值为:%f\n", *pFloat);
    	return 0;
    }
    
  • 可能大部分小伙伴都会认为,打印的结果分别是:

    n的值为:9
    pFloat的值为:9.000000
    n的值为:9
    *pFloat的值为:9.000000
    
  • 实际上,运行得到的结果为:

    n的值为:9
    pFloat的值为:0.000000
    n的值为:1091567616
    *pFloat的值为:9.000000
    
    • 可以看到,操作float* pFloat = (float*)&n,我们将整型n以浮点数的形式存入float型指针pFloat中,然后发现printf("pFloat的值为:%f\n", *pFloat)打印结果错误
    • 当我们执行*pFloat = 9.0这一操作,即向整型n中存入float型的值,发现printf("n的值为:%d\n", n)打印结果错误。
  • 由此我们可以知道,在内存中,浮点数和整数的存储形式是不一样的,那具体有哪些差别呢?

浮点数的表示形式

  • 根据国际标准IEEE(电器和电子工程协会)754任意一个二进制浮点数v都可以表示为这个形式:(-1)^S * M * 2 ^ E

    • (-1) ^ S表示符号位,当S为0时表示正数,当S为1时表示负数。
    • M表示有效数字,大于等于1,小于2
    • 2 ^ E表示指数位
  • 举个例子:

    • 例如十进制浮点数5.5
    • 其二进制形式为:101.1(如果不会怎么转换,请看二进制、八进制、十六进制与十进制的相互关系)
    • 再用科学计数法表示:1.011 * 22
    • 加上符号位:(-1)0 * 1.011 * 22
    • 即这里的S = 0,M = 1.011,E = 2
  • 因此,对浮点数进行存储时,只需要对S,M,E这三个数进行存储就行了

浮点数的存储

  • IEEE 754规定:

  • 对于32位的浮点数(float型),最高的1位是符号位S,接着的8位是指数E,剩下的23位是有效数字M

在这里插入图片描述

  • 对于64位的浮点数(double型),最高的1位是符号位S,接着的11位是指数E,剩下的52位是有效数字M

在这里插入图片描述

  • IEEE 754对有效数字M和指数E还有些特殊的规定:

  • 对于有效数字M:

    • 之前说过,1<=M<2因此M总是可以写成1.XXXXX这样的形式
    • 754规定,在计算机内部保存有效数字M时,默认这个数的第一位总是1,因此可以被舍去,只保留后面的xxxxxx部分。
    • 等到读取的时候,再把第一位的1加上去。
    • 这样做,就可以节省一位有效数字,以double型为例,原本M为52,只能存储52为有效数字,但舍去第一位后,就相当于可以存储53位数字了。
  • 对于指数E:

    • 首先我们需要知道,指数E为一个无符号整数(unsigned int)

    • 以float型为例,E所能表示的范围是0~255。但问题是,在实际的表示中,指数E也可能存在负数的情况,例如十进制浮点数0.5,转换为二进制为0.1,则S = 1,E = -1,M = 1,那么怎么处理E为负数的情况呢?

    • IEEE 754规定,存入内存时E的真实值必须再加上一个中间数,对于8位的E,这个中间数是127,对于11位的E,这个中间数是1023。例如对于十进制浮点数0.5,存入内存时,E的值应该是-1 + 127 = 126

    • 将指数E从内存取出要分以下三种情况:

      E不全为零且E不全为1
      • 这时,指数E的真实值就是存储值减去中间值(127/1023)

      • 有效数字M需要加上第一位的1。

      E全为0
      • 这时,指数E的真实值就是(1 - 127) / (1 - 1023)(标准规定)

      • 有效数字M不需要加上第一位的1,而是还原为0.xxxxxxx的小数这样做是为了表示±0,以及几近于0的极小的数字(标准规定)

      E全为1
      • 这时,有效数字M全为0,表示±∞(正负取决于符号位)(标准规定)

Eg

对于十进制浮点数5.5

十进制float-> 5.5
二进制 -> 101.1
二进制科学技术 -> 1.011 * 2 ^ 2
加上符号位 -> (-1) ^ 0 * 1.011 * 2 ^ 2
S = 0, M = 1.011, E = 2
存入到二进制序列:0 10000001 01100000000000000000000
转换为十六进制:4 0 B 0 0 0 0 0

在这里插入图片描述

总结

  • 我们再来回顾上面引例的代码:

    #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("n的值为:%d\n", n);
    	printf("*pFloat的值为:%f\n", *pFloat);
    	return 0;
    }
    
  • 整数9在内存中的存储形式为0000 0000 0000 0000 0000 0000 0000 1001

  • 我们用float类型的指针pFloat指向这段整型内存,那么根据上面的知识,我们可以得到:

    在这里插入图片描述

  • 指数E全为0,因此printf("pFloat的值为:%f\n", *pFloat);打印出来的结果就是pFloat的值为:0.000000

  • 之后,将这段内存改为浮点数9.0

  • 9.0的二进制序列为:1001.0

  • 写成科学计数法为:(-1) ^ 0 * 1.001 * 2 * 3,即S = 0, E = 3, M = 1.001(存入内存时,E = 127 + 3 = 130,M = 0.001),即:0 10000010 00100000000000000000000

    在这里插入图片描述

  • 如果我们用读取整数的方法来读取这段内存,那结果就是1,091,567,616也就是printf("n的值为:%d\n", n)的打印结果

用指针改变内存和强制转换的区别

  • 通过对浮点数在内存中如何储存的了解,我们应该知道,用指针改变内存的读取和强制转换变量类型是两个截然不同的操作

  • 强制转换类型:

    float num_1 = 3.14;
    int num_2 = (int)num_1;
    printf("%d\n",num_2);
    /*
    	将float强制转换为int只是简单的对小数点之后的数据进行舍去,而没有改变对内存的处理
    	打印结果为3.
    */
    
  • 指针改变内存

    float num_1 = 9.5;
    int *num_2 = (int *)&num_1;
    printf("%d\n",*num_2);
    /*
    	用读取整型数据的方式读取float型的内存
    	打印结果为1092091904
    */
    

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

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

相关文章

【来不及刷题之】33、合并区间(+ 删除被覆盖区间)

1. 删除被覆盖区间 先来看与合并区间比较相似的一道题目&#xff1a;删除被覆盖的区间 思路&#xff1a; 起点按照升序排序&#xff0c;终点按照降序排序基于以上排序规则&#xff0c;相邻的两个区间会有以下三种情况&#xff1a; 第一种情况&#xff1a;找到一个覆盖区间&a…

axios封装时对config参数的一点思考

目 录 0、起因1、冷静分析2、一个简单粗糙但是能用的封装 0、起因 创建一个实例&#xff1a; const service axios.create({baseURL: "/api",timeout: 3e3,withCredentials: true,headers:{"Content-Type": "application/json"} })简单封装&a…

STM32控制OLED介绍

OLED&#xff0c;即有机发光二极管&#xff08;Organic Light-Emitting Diode&#xff09;&#xff0c;又称为有机电激光显示&#xff08;Organic Electroluminesence Display&#xff0c; OELD&#xff09;。 OLED 由于同时具备自发光&#xff0c;不需背光源、对比度高、 厚度…

人工智能(pytorch)搭建模型7-利用pytorch搭建一个BiLSTM+CRF模型,实现简单的命名实体识别

大家好&#xff0c;我是微学AI&#xff0c;今天给大家介绍一下人工智能(pytorch)搭建模型7-利用pytorch搭建一个BiLSTMCRF模型&#xff0c;实现简单的命名实体识别&#xff0c;BiLSTMCRF 模型是一种常用的序列标注算法&#xff0c;可用于词性标注、分词、命名实体识别等任务。本…

软件安全概述

软件定义是&#xff1a;计算机程序、规则和可能相关的文档。 软件是程序、数据和文档的集合体。 零日漏洞、零日攻击 零日漏洞是指未被公开披露的软件漏洞&#xff0c;没有给软件的作者或厂商以时间去为漏洞打补丁或是给出建议解决方案&#xff0c;从而攻击者能够利用这种漏洞破…

ROS:话题消息(Message)的定义与使用

目录 一、话题模型二、自定义话题消息2.1定义msg文件2.2在package.xml中添加功能包依赖2.3在CMakeLists.txt中添加编译选项2.4编译生成C头文件或Python库 三、创建代码并编译运行&#xff08;C&#xff09;3.1创建代码3.2编译 四、运行 一、话题模型 自定义一个消息类型“Pers…

python接口测试之测试报告

在本文章中&#xff0c;主要使用jenkins和编写的自动化测试代码&#xff0c;来生成漂亮的测试报告&#xff0c;关于什么是CI这些我就不详细的介绍了&#xff0c;这里我们主要是实战为主。 首先搭建java的环境&#xff0c;这个这里不做介绍。搭建好java的环境后&#xff0c;在h…

Python:Python编程:从入门到实践__超清版:Python标准库:线程

Python线程与安全 实现线程安全有多重方式&#xff0c;常见的包括&#xff1a;锁&#xff0c;条件变量&#xff0c;原子操作&#xff0c;线程本地存储等。 &#x1f49a; 1. 锁2. 条件变量3. 通过 join 阻塞当前线程4. 采用 sleep 来休眠一段时间5. 原子操作5.1 使用 threading…

【I2C】Linux I2C子系统分析

文章目录 一、I2C体系架构二、主要的结构体1. i2c_adapter2. i2c_algorithm3. i2c_driver4. i2c_client4.1 方式一&#xff1a;通过I2C bus number静态方式来创建4.2 方式二&#xff1a;通过Device Tree来创建4.3 方式三&#xff1a;直接通过i2c_new_device来创建4.3 方式四&am…

openEuler22.03制作openstack平台使用的镜像

系列文章目录 第一章 openEuler22.03制作openstack平台使用的镜像 文章目录 系列文章目录前言一、virt-manager上的准备工作1、网卡类型切换为virtio2、IDE驱动设置成Virtio3、Display设置成vnc3、虚拟机系统分区 二、安装普通工具包三、安装云化工具包1、安装工具包2、修改配…

数字化转型,企业为什么要转型?如何转型?

数字化转型是利用数字化技术&#xff08;例如云计算、大数据、人工智能、物联网、区块链等&#xff09;和能力来驱动组织商业模式创新和商业生态系统重构的途径和方法即是数字化转型。其目的是实现企业业务的转型、创新、增长。 核心强调了两点&#xff0c;其一是数字化技术的应…

每日一练 | 华为认证真题练习Day51

1、如下图所示&#xff0c;IPSec传输模式中AH的头部应该插入到以下哪个位置&#xff1f; A. 1 B. 2 C. 3 D. 4 2、以下哪种远程登录方式最安全&#xff1f; A. Telnet B. Stelnet v100 C. Stelnet v2 D. Stelnet v1 3、以下业务模块的ACL默认动作为permit的是&#xff1…

玩转 ChatGPT,看这条就够了,Prompt 最全中文合集

Prompt 最全中文合集 玩转 ChatGPT&#xff0c;看这条就够了&#xff01; &#x1f680; 简化流程&#xff1a;ChatGPT Shortcut 提供了快捷指令表&#xff0c;可以快速筛选和搜索适用于不同场景的提示词&#xff0c;帮助用户简化使用流程。 &#x1f4bb; 提高生产力&#…

CSDN打出各种数学符号和数学公式

目录 1、基本四则运算2、指数对数3、根号、省略号、向量4、大&#xff08;小&#xff09;于等于号5、特殊符号、希腊字母符号6、累加累乘7、矩阵8、更改公式中的颜色 我们在用CSDN打出各种数学符号和数学公式时&#xff0c;需要学习一些关于LaTex的语法&#xff0c;在此做一个记…

java数组学习

一、数组的概述 1.数组的理解:数组(Array),是多个相同类型数据按一定顺序排列的集合&#xff0c; 并使用一个名字命名&#xff0c;并通过编号的方式对这些数据进行统一管理。 2.数组相关的概念: >数组名 >元素 >角标、下标、索引 >数组的长度&#xff1a;元素…

联通云数据库CUDB:基于openGauss打造新一代自主创新云原生数据库

总体概述 联通云彰显央企担当&#xff0c;围绕国家对信息技术基础软件的政策要求&#xff0c;开展数据库自主研发。在openGauss开源社区版软件基础上&#xff0c;聚焦政企市场&#xff0c;坚持内核创新&#xff0c;完善工具生态&#xff0c;基于海量云存储能力、存算分离架构…

React中的懒加载以及在Ice中实践

您好&#xff0c;如果喜欢我的文章&#xff0c;可以关注我的公众号「量子前端」&#xff0c;将不定期关注推送前端好文~ 前言 对于页面性能优化&#xff0c;组件懒加载是个比较不错的方案&#xff0c;并且在整个项目打包后&#xff0c;如果未做代码分割&#xff0c;构建出的文…

代理ip的优势、用途及注意事项

随着互联网的高速发展&#xff0c;代理ip的名气和地位也随着水涨船高。那么是什么让它们被我们所知悉的呢&#xff1f;下面我们就代理ip的优势、用途和注意事项来分析一下它为什么能迎合着互联网的发展而壮大自己的。 一、优势 每一个脱颖而出的产品必然有它的优势&#xff0c;…

Axure教程—菜单(中继器)

本文将教大家如何用AXURE中的中继器制作菜单&#xff08;自动折叠其他菜单&#xff09; 一、效果介绍 如图&#xff1a; 预览地址&#xff1a;https://iuek50.axshare.com 下载地址&#xff1a;https://download.csdn.net/download/weixin_43516258/87854640?spm1001.2014.30…

知识图谱简介

什么是知识图谱&#xff1f; 参考&#xff1a;知识图谱1、知识图谱2 本质上&#xff0c;知识图谱主要目标是用来描述真实世界中存在的各种实体和概念&#xff0c;以及他们之间的关系&#xff0c;因此可以认为是一种语义网络。 主要作用&#xff1a;通过数据&#xff0c;建立图…