探索浮点数在内存中的存储(附带快速计算补码转十进制)

目录

一、浮点数在内存中的存储

1、常见的浮点数:

2、浮点数存储规则:

3、内存中无法精确存储:

4、移码与指数位E:

5、指数E的三种情况:

二、快速计算补码转十进制

1、第一种方法讨论:

2、第二种方法讨论:

3、第三种方法讨论:

4、第四种方法讨论:

一、浮点数在内存中的存储

1、常见的浮点数:

首先C语言中的浮点数是什么呢?说白了就是小数,这样的小数在C语言中主要有两种表示:

3.14159//这种是常见的浮点数,以小数形式出现
3.14E10//这种是科学计数法中的表示,浮点数为3.14×10^10,或写成3.14E+10

      既然整型在计算机中有表示范围,那么浮点数其实也是有范围的,我们可以在<limits.h>文件和<float.h>文件中查找到整型和浮点型的表示范围。那么整型和浮点型在内存中的存储相同吗?我们根据一段代码进行分析。

 int main(){int n = 9;float* pf = (float*)&n;printf("n = %d\n", n);//打印结果为:n = 9printf("*pf = %f\n", *pf);//打印结果为:*pf = 0.000000​*pf = 9.0;printf("n = %d\n", n);//打印结果为:n = 1091567616printf("*pf = %f\n", *pf);//打印结果为:*pf = 9.000000return 0;}

注:上面的代码是在VS编译器的x64环境下的运行结果。

       这个代码得到了让人意想不到的结果,第1条和第4条printf()语句的输出结果自然不必多说,那么第2条和第3条的执行结果说明,整型数据和浮点型数据的存储方式是不同的

2、浮点数存储规则:

       根据国际标准IEEE754,可以将任意一个浮点数V都写成(-1)^S×M×2^E,其中(-1)^S表示符号位,当S为0时,V为正数,S为1时,V为负数。M表示有效数字,规定范围在1和2之间,2^E表示指数位。

补充:探究二进制整数和小数的权重

    1111 1111 . 1111,整数部分的权重由小到大(从右向左)依次是2^0=1、2^1=2、2^2=4、2^3=8、2^4=16、2^5=32、2^6=64、2^7=128,小数部分权重由大到小(从左向右)依次是2^(-1)=0.5、2^(-2)=0.25、2^(-3)=0.125、2^(-4)=0.0625。

        比如5.5,转换成二进制是101.1,这样表示不是科学计数法所规定的,我们将小数点向左移动两位(称为左规两位),变成1.011×2^2(左规次方为正数,右规次方为负数,这样看,V=(-1)^0×1.011×2^2,其中S=0,M=1.011,E=2。

        这里我们就可以看出,为什么任何一个浮点数的M都是在1到2之间?因为对于任意一个数转换成二进制,最高位一定是1(因为如果是0可以不写),这样左规几次之后,M得到的值一定在1和2之间。

      下面将5.5存储到内存中,IEEE754规则如下,对于32位浮点数(float类型),使用1个比特位用来存放符号位S,用8个比特位存放指数位E,剩下的23个比特位存放有效数字M,但由于M的范围确定,所以最高位的1(整数部分的1)通常不需要存储,所以实际上可以存储24位有效数字。对于64位浮点数(double类型),使用1个比特位存放符号位S,11个比特位存放指数位E,剩下的52个比特位存放有效数字M,同样省略了M的最高位。

3、内存中无法精确存储:

       对于一些浮点数,计算机是无法准确进行存储的,比如5.3,它的小数部分0.3转换成二进制可能有非常多的位数(0.3的二进制为 0.010011 0011 0011...,1001部分无限循环),所以计算机存储的也是近似的数据。

//浮点数在内存中的存储int main(){float f = 5.3f;return 0;}

4、移码与指数位E:

       首先,E表示指数位,就一定是一个无符号整数,如果E有8位,那么范围是0~255,E有11位,范围是0~2047。但是E有可能是负数,比如0.5的科学计数法表示为1.0×2^(-1),此时E=-1。

       为了避免这种情况的发生,我们将8位的E加上偏移量127,11位的E加上1023,这样就可以保证E一定为正数,这样的E的二进制表示称为移码表示,所以我们可知,加上偏移量的E的移码就一定为正数,但移码不止在浮点数中表示阶码,其他地方也有所体现,所以移码不一定为正数。

//浮点数在内存中的存储int main(){float f = 5.5f;//5.5的二进制表示101.1,科学计数法表示1.011×2^2//S=0(正数),M=011 0000 0000 0000 0000 0000,E=2+127//二进制0 1000 0001 011 0000 0000 0000 0000 0000//对齐后结果0100 0000 1011 0000 0000 0000 0000 0000//对应的十六进制40 b0 00 00return 0;}

下面分析我们来最初的代码,代码如下。

int main(){int n = 9;float* pf = (float*)&n;printf("n = %d\n", n);//打印结果为:n = 9printf("*pf = %f\n", *pf);//打印结果为:*pf = 0.000000​*pf = 9.0;printf("n = %d\n", n);//打印结果为:n = 1091567616printf("*pf = %f\n", *pf);//打印结果为:*pf = 9.000000return 0;}

先看第3条printf()语句。

//第3条printf()语句相当于下面的代码int main(){float f = 9.0f;//9.0的二进制是1001.0,科学计数法表示为1.0010×2^3//S=0,M=001 0000 0000 0000 0000 0000,E=3+127//二进制为0 1000 0010 001 0000 0000 0000 0000 0000//整理后0100 0001 0001 0000 0000 0000 0000 0000//十六进制为41 10 00 00//为什么输出1091567616呢?//这是因为计算机把0100 0001 0001 0000 0000 0000 0000 0000当成有符号数//2^20+2^24+2^30=1048576+16777216+1073741824=1091567616return 0;}

5、指数E的三种情况:

如果拿到一串浮点数的二进制数,计算E就分为下面三种情况。

1)E不为全0,也不是全1:

     拿到E的部分,比如1000 0001,用这个数的十进制减去127后,得到的就是E原来的值,即129-127=2,原来的E=2。

2)E为全0:

       此时的E如果是8位,则真实值是1-127=-126,如果是16位,则真实值是1-1023=-1022,实际上这个小数已经是非常非常小的一个数了,无限接近于0,那么计算机会将这样的小数按0来处理,这时的有效数字M不再加上第一位的1,而是还原为0.xxxxx...的小数,这样做为了表示±0。也就是E、M全0时,这样的小数就按0处理。

3)E为全1:

如果E全为1,此时M全为0,这样的数字表示±∞,正负取决于符号位S。

这样特殊的数就有8种表示:

SEM
000+0
100-0
00≠0非规格化正数
10≠0非规格化负数
010+∞
110-∞
01≠0NaN
11≠0NaN

非规格化数字是计算机中一种特殊的数字。

NaN(非数)也是一种特殊的数字,含义是无定义的数或不可表示的数,比如0/0、∞/∞、∞-∞等等返回的都是一个非数。

下面就可以分析上面代码的第2条printf()语句了。

//第2条printf()语句int main(){int n = 9;printf("%f\n", n);//打印结果为:0.000000//9的二进制为0000 0000 0000 0000 0000 0000 0000 1001//其中E的部分是0000 0000,全为0//M的部分是000 0000 0000 0000 0000 1001//科学计数法表示为0.000 0000 0000 0000 0000 1001 × 2^(-126),这已经是非常小的数字了//而且%f只能打印小数点后六位,结果自然是0.000000printf("%f\n", (float)n);//打印结果为:9.000000return 0;}

二、快速计算补码转十进制

       首先,只有负数才有补码,正数的补码就是原码,直接按照原码转换成十进制即可,所以下面计算的均是负数的补码转成十进制(《最快10秒钟就可以完成》)

我们先看如何转换,之后在讨论是为什么这么转换。

目前,拿到一个负数的补码,我知道的有4种方法可以转换成十进制:

1、补码转成原码,再转换成十进制,比如1100 0101[补] 转成 1011 1011[原],再转换成十进制就是-(1+2+8+16+32)=-59

2、用最高位的位权依次加上其他位的值,比如1100 0101转换成十进制,-128+(1+4+64)=-59

3、先求无符号数,再用256减去无符号数的值,最后取负数即可,比如1100 0101转换成十进制,-(256-(1+4+64+128))=-59

4、直接按照补码进行计算,按照有0的位权相加,再加1,最后取负(这是我认为最快的方法),比如1100 0101转换成十进制,-(2+8+16+32+1)=-59

      个人觉得第1种最常用,但是比较慢,第2、3种实际上差不多(第三种甚至比第二种还慢),第4种最快(因为对我们来说,取负操作要比含有负数的减法更受欢迎)。

       下面补充一种更快的计算数字相加的方法,是不是经常对于1+2+8+16+32这样的一串数字相加比较头疼,今天快速计算方法,8以下的数字一起计算(一眼就能算出结果),128以下的数字一起计算(绝大多数都是最大计算到128),那么剩下的就是8+16+32+64这种,只需将16看成2×8,32看成4×8,64看成6×8即可,比如1+2+8+16+32=3+(1+2+4)×8=56+3=59

下面来讨论到底是为什么可以这么转换,如果对这里不感兴趣的同学,现在可以划走了~

1、第一种方法讨论:

       实际上,我也不知道为什么要这样转换,在学计算机的时候,就接触到这种方法了,但是用着用着感觉比较慢,于是开发出了其他的方法。

2、第二种方法讨论:

      对于正数的原码,其实每一位是有对应的位权的,比如1001[原],对应的十进制就是2^0+2^3=9。我们知道,对于负数的补码来说,二进制的最高位表示符号位,它的位权是一个负值,比如1001[补],对于的十进制就是-2^3+2^1=-7(每一位的位权不变,只是最高位是个负数)。

3、第三种方法讨论:

这种方法是我观察char类型和unsigned char类型数据的规律中得出的,数据如下:

char二级制(补)十进制unsigned char二级制(补)十进制
0000 000000000 00000
0000 000110000 00011
0000 001020000 00102
0000 001130000 00113
............
0111 11101260111 1110126
0111 11111270111 1111127
1000 0000-1281000 0000128
1000 0001-1271000 0001129
............
1111 1100-31111 1100253
1111 1110-21111 1110254
1111 1111-11111 1111255

       不难看出,char类型可以存储127个正数,128个负数,还有一个0,一共256个数据,所能表示的范围是-128~127。unsigned char类型可以存储255个正数和一个0,也是256个数据,所能表示的范围是0~255。

       同时上面的两组数据也有一些规律,观察有符号和无符号的十进制,当最高位为1时,对应的绝对值之和总是256,比如1000 0001[补]是-127[有]或129[无],可以写成-127 = -(256-129),而129用2^7+2^1计算。

       那么以后计算有符号数比较麻烦时,可以这样快速计算,即用256减去对应的无符号数,再取负,得到对应的有符号数。比如1011 0110[补],无符号数是2^1+2^2+2^4+2^5+2^7=2+4+16+32+128=182,有符号数就是-(256-182)=-74。

这种计算方法的优点是把对应的无符号数也计算出来了,缺点还是比较慢。

4、第四种方法讨论:

       还有一种计算有符号数的方法,观察1000 0000 + 0111 1111结果为1111 1111,十进制为-1,我们可以找镜像,即把这个负数变成对应的正数(1变0,0变1),比如1011 0110,镜像过去的正数是0100 1001,用-1减去这个正数得到的就是负数,即-1-(1+8+64)=-74

      这种方法与原反补转换计算有符号数几乎相同,但我们可以找捷径计算,直接拿到有符号数1011 0110,按照位权相加,此时要看有0的位置,即1+8+64=73,最后加1再取负数,即-(73+1)=-74,得到的就是有符号数的十进制大小。

        第四种说白了就是既然转成原码后可以按照“1”位置的位权相加,那么如果不转成原码呢?我们就可以看“0”位置的位权相加(反正一个负数的二进制最高位是1,看“0”的位权没有影响)。之后转成原码要加1,那我们不转也要加1(是不是有同学想成减1了,其实这时候不加1的值和原码不加1计算出来的值是一样的),最后整体加个负数即可。听懂掌声~

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

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

相关文章

Windows版FFmpeg使用及B站视频下载示例python源码

Windows版FFmpeg使用及B站视频下载示例python源码 FFmpeg介绍和下载 FFmpeg 是一个功能强大、灵活且广泛使用的多媒体处理工具&#xff0c;无论是在专业领域还是日常使用中&#xff0c;都能满足各种多媒体处理需求。FFmpeg 是一个开源项目&#xff0c;遵循 LGPL 或 GPL 许可。…

最新版 (持续更新)docker 加速源 linux yum 源

收藏两个网站&#xff0c;配置docker 加速源与yum 源。 docker 加速源链接 Docker/DockerHub 国内镜像源/加速列表&#xff08;2月25日更新-长期维护&#xff09;-腾讯云开发者社区-腾讯云https://cloud.tencent.com/developer/article/2485043 yum 源 CentOS7停服后yum源配置…

计算机三级网络技术备考(2)

#第二章关于中小型网络系统总体规划与设计方法 考点一&#xff1a;网络总体设计 考点二网络关键设备选型 考点三服务器的选型 #第三章&#xff1a;IP地址规划设计技术 考点1&#xff1a;二进制和十进制转换 小练习记得做昂 考点2&#xff1a;IP地址分类及规划方法 A类&#x…

【代码软件 | vs2019】vs2019+Qt5.12.12开发环境 的下载、安装详细介绍

&#x1f601;博客主页&#x1f601;&#xff1a;&#x1f680;https://blog.csdn.net/wkd_007&#x1f680; &#x1f911;博客内容&#x1f911;&#xff1a;&#x1f36d;嵌入式开发、Linux、C语言、C、数据结构、音视频&#x1f36d; &#x1f923;本文内容&#x1f923;&a…

【DeepSeek探索篇(四)】高效学习与工作,从搭建DeepSeek个人知识库开始!

高效学习与工作&#xff0c;从搭建DeepSeek个人知识库开始&#xff01; 打工人和学习者必看&#xff01;你为何急需一个个人知识库&#xff1f;搭建个人知识库需要用到哪些技术呢&#xff1f;一、模型微调与RAG技术简介二、RAG技术原理剖析 揭秘Embedding&#xff1a;为何它是D…

AI数字人系统源码部署解决方案!!!

一、开场白 如今&#xff0c;科技的步伐越来越快&#xff0c;数字人已经从想象中走进了我们的现实生活。它们在娱乐、教育、医疗等多个领域大放异彩。了解数字人的代码开发技术&#xff0c;能让我们更好地理解其工作原理&#xff0c;为那些想在这一领域大展拳脚或者用数字人技…

【网络】HTTPS协议原理

文章目录 1. HTTPS 是什么2. 常见的加密方式3. 数据摘要4. 加密方案测试4.1 只是用对称加密4.2 只使用非对称加密4.3 双方都使用非对称加密4.4 对称 非对称 5. 证书5.1 数据签名5.2 CA 证书5.3 方案五 非对称加密 对称加密 证书认证 1. HTTPS 是什么 HTTP 协议内容都是按照…

计算机网络————(三)

前文二 前文一 Websocket协议 是一种存在TCP协议之上的协议 当客户端需要了解服务器是否更新就需要不断给客户端发送请求询问是否更新&#xff0c;这行会造成服务端压力很大 而Websocket相当于服务器一旦更新了就会给客户端发送消息表明自己更新了&#xff0c;类似客户端订阅…

Blueprint —— Events

目录 一&#xff0c;Event Level Reset 二&#xff0c;Event Actor Begin Overlap 三&#xff0c;Event Actor End Overlap 四&#xff0c;Event Hit 五&#xff0c;Event Any Damage 六&#xff0c;Event Point Damage 七&#xff0c;Event Radial Damage 八&#xff…

博云先进算力管理平台AIOS已上线全尺寸DeepSeek系列模型

在异构基础设施上轻松运行全尺寸DeepSeek DeepSeek于2024年12月发布了包括 DeepSeek V3、R1、Janus Pro等多版本模型。V3版本适用于通用型自然语言处理任务&#xff0c;R1专注于复杂推理任务&#xff0c;而 Janus Pro 则擅长多模态理解与生成&#xff0c;可满足企业不同 AI 场…

如何搭建起成熟的团队知识文档管理系统

搭建一个成熟的团队知识文档管理系统&#xff0c;对于提升团队的工作效率、促进信息共享以及保障知识资产的安全性都至关重要。**要实现这一目标&#xff0c;首先需要明确知识管理的目标和需求&#xff0c;其次选择合适的知识管理工具&#xff0c;最后制定有效的管理流程和权限…

0-基于强化学习的图Transformer算法求解车辆路径问题(2023)

文章目录 Abstract1 Introduction2. Related Work2.1 引言2.2.基于RNN的VRP解决方案2.3.基于GNN的VRP解决方案2.4.基于Transformer的车辆路径问题求解方法3 边嵌入注意力模型3.1 编码器3.1.1 边嵌入多头注意力3.1.2. 前馈网络(FFN)、批量归一化和残差连接3.2 解码器3.2.1 解码…

C#快速调用DeepSeek接口,winform接入DeepSeek查询资料 C#零门槛接入DeepSeek C#接入DeepSeek源代码下载

下载地址<------完整源码 在数字化转型加速的背景下&#xff0c;企业应用系统对智能服务的需求日益增长。DeepSeek作为先进的人工智能服务平台&#xff0c;其自然语言处理、图像识别等核心能力可显著提升业务系统的智能化水平。传统开发模式下&#xff0c;C#开发者需要耗费大…

有关与 WSL 2 的主要区别的信息,请访问 https://aka.ms/wsl2

https://learn.microsoft.com/zh-cn/windows/wsl/install-manual#step-4—download-the-linux-kernel-update-package

深度神经网络(DNN)编译器原理简介

深度神经网络(DNN)编译器原理简介 目录 深度神经网络(DNN)编译器原理简介1 什么是DNN编译器2 前端3 后端4 中间表达&#xff08;Intermediate Representation&#xff0c;后文用IR代替&#xff09;5 优化过程6 计算图优化6.1 表达式化简6.2 公共子表达式消除6.3 常数传播6.4 矩…

最小二乘法与梯度下降(原理)

一、最小二乘法 损失函数矩阵形式&#xff1a; 令导数loss0 &#xff0c;可解得&#xff1a; API : sklearn.linear_model.LinearRegression() 功能&#xff1a; 普通最小二乘法线性回归, 权重和偏置是直接算出来的&#xff0c;对于数量大的不适用&#xff0c;因为计算量…

【2025全网最新最全】前端Vue3框架的搭建及工程目录详解

文章目录 安装软件Node.js搭建Vue工程创建Vue工程精简Vue项目文件 Vue工程目录的解读网页标题的设置设置全局样式路由配置 安装软件Node.js 下载地址&#xff1a;https://nodejs.org/zh-cn/ 安装完成后&#xff0c;打开cmd,查看环境是否准备好 node -v npm -vnpm使用之前一定…

Java 之集成 DataX 数据同步工具

1、官网下载 DataX https://github.com/alibaba/DataX 2、将依赖添加到本地&#xff08;DataX没有maven坐标&#xff0c;需要自己安装&#xff09; mvn install:install-file -Dfile"datax-common-0.0.1.jar" "-DgroupIdcom.datax" "-DartifactIdda…

OpenEuler学习笔记(三十五):搭建代码托管服务器

以下是主流的代码托管软件分类及推荐&#xff0c;涵盖自托管和云端方案&#xff0c;您可根据团队规模、功能需求及资源情况选择&#xff1a; 一、自托管代码托管平台&#xff08;可私有部署&#xff09; 1. GitLab 简介: 功能全面的 DevOps 平台&#xff0c;支持代码托管、C…

pikachu

暴力破解 基于表单的暴力破解 【2024版】最新BurpSuit的使用教程&#xff08;非常详细&#xff09;零基础入门到精通&#xff0c;看一篇就够了&#xff01;让你挖洞事半功倍&#xff01;_burpsuite使用教程-CSDN博客 登录页面&#xff0c;随意输入抓包&#xff0c;发送到攻击…