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

目录

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

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 = 9
     printf("*pf = %f\n", *pf);//打印结果为:*pf = 0.000000
 ​
     *pf = 9.0;
     printf("n = %d\n", n);//打印结果为:n = 1091567616
     printf("*pf = %f\n", *pf);//打印结果为:*pf = 9.000000
     return 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 00
     return 0;
 }

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

int main()
 {
     int n = 9;
     float* pf = (float*)&n;
     printf("n = %d\n", n);//打印结果为:n = 9
     printf("*pf = %f\n", *pf);//打印结果为:*pf = 0.000000
 ​
     *pf = 9.0;
     printf("n = %d\n", n);//打印结果为:n = 1091567616
     printf("*pf = %f\n", *pf);//打印结果为:*pf = 9.000000
     return 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=1091567616
     return 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.000000
     printf("%f\n", (float)n);//打印结果为:9.000000
     return 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计算出来的值是一样的),最后整体加个负数即可。听懂掌声~

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

实体机器人识别虚拟环境中障碍物

之前的内容已经实现了虚拟机器人识别实体机器人的功能&#xff0c;接下来就是实体机器人如何识别虚拟环境中的障碍物&#xff08;包括虚拟环境中的障碍物和其他虚拟机器人&#xff09;。 我做的是基于雷达的&#xff0c;所以主要要处理的是雷达的scan话题 我的虚拟机器人命名…

湖北中医药大学谱度众合(武汉)生命科技有限公司研究生工作站揭牌

2025年2月11日&#xff0c;湖北中医药大学&谱度众合&#xff08;武汉&#xff09;生命科技有限公司研究生工作站揭牌仪式在武汉生物技术研究院一楼101会议室举行&#xff0c;湖北中医药大学研究生院院长刘娅教授、基础医学院院长孔明望教授、基础医学院赵敏教授、基础医学院…

ARM Coretex-M核心单片机(STM32)找到hardfault的原因,与hardfault解决方法

1. 前提基础知识&#xff08;ARM异常 压栈流程&#xff09;M核栈增长是地址逐渐减小的 **M3h ARM CM4核心带浮点处理器FPU的&#xff0c;压栈的东西还不一样 进入hardfult后看MSP或者SP的值&#xff0c;看下边第二章图如果hardfult里边啥都没有&#xff0c;就只有个while(1){}…

组件传递props校验

注意&#xff1a;prop是只读的&#xff01;不可以修改父组件的数据。 可以检验传过来的内容是否类型没问题。 App.vue <template><div><!-- <parentDemo/> --><componentA/></div></template> <script> import ComponentA …

机试刷题_NC52 有效括号序列【python】

NC52 有效括号序列 from operator import truediv # # 代码中的类名、方法名、参数名已经指定&#xff0c;请勿修改&#xff0c;直接返回方法规定的值即可 # # # param s string字符串 # return bool布尔型 # class Solution:def isValid(self , s: str) -> bool:if not s…

threejs:document.createElement创建标签后css设置失效

vue3threejs&#xff0c;做一个给模型批量CSS2D标签的案例&#xff0c;在导入模型的js文件里&#xff0c;跟着课程写的代码如下&#xff1a; import * as THREE from three; // 引入gltf模型加载库GLTFLoader.js import { GLTFLoader } from three/addons/loaders/GLTFLoader.…

一文读懂西门子 PLC 串口转以太网系列模块

在工业自动化领域&#xff0c;随着智能化和信息化的不断发展&#xff0c;设备之间的高效通信变得至关重要。西门子 PLC 作为工业控制的核心设备&#xff0c;其通信方式的拓展需求日益凸显。西门子 PLC 串口转网口产品应运而生&#xff0c;它为实现串口设备与以太网网络的无缝连…

Linux | GRUB / bootloader 详解

注&#xff1a;本文为 “Linux | GRUB / bootloader” 相关文章合辑。 英文引文&#xff0c;机翻未校。 图片清晰度限于引文原状。 未整理去重。 What is Grub in Linux? What is it Used for? Linux 中的 Grub 是什么&#xff1f;它的用途是什么&#xff1f; Abhishek …

java高级(IO流多线程)

file 递归 字符集 编码 乱码gbk&#xff0c;a我m&#xff0c;utf-8 缓冲流 冒泡排序 //冒泡排序 public static void bubbleSort(int[] arr) {int n arr.length;for (int i 0; i < n - 1; i) { // 外层循环控制排序轮数for (int j 0; j < n -i - 1; j) { // 内层循环…

Dubbo RPC 原理

一、Dubbo 简介 Apache Dubbo 是一款高性能、轻量级的开源 RPC 框架&#xff0c;支持服务治理、协议扩展、负载均衡、容错机制等核心功能&#xff0c;广泛应用于微服务架构。其核心目标是解决分布式服务之间的高效通信与服务治理问题。 二、Dubbo 架构设计 1. 核心组件 Prov…

普中单片机-51TFT-LCD显示屏(1.8寸 STM32)

普中官方论坛&#xff1a; http://www.prechin.cn/gongsixinwen/208.html 普中科技-各型号开发板资料链接&#xff1a;https://www.bilibili.com/read/cv23681775/?spm_id_from333.999.0.0 27-TFTLCD显示实验_哔哩哔哩_bilibili 2.程序烧录 2.1设置彩屏驱动 3.实验效果

Starlink卫星动力学系统仿真建模第九讲-滑模(SMC)控制算法原理简介及卫星控制应用

滑模控制&#xff08;Sliding Mode Control&#xff09;算法详解 一、基本原理 滑模控制&#xff08;Sliding Mode Control, SMC&#xff09;是一种变结构控制方法&#xff0c;通过设计一个滑模面&#xff08;Sliding Surface&#xff09;&#xff0c;迫使系统状态在有限时间内…

nss刷题5(misc)

[HUBUCTF 2022 新生赛]最简单的misc 打开后是一张图片&#xff0c;没有其他东西&#xff0c;分离不出来&#xff0c;看看lsb&#xff0c;红绿蓝都是0&#xff0c;看到头是png&#xff0c;重新保存为png&#xff0c;得到一张二维码 扫码得到flag [羊城杯 2021]签到题 是个动图…

【C/C++】删除链表的倒数第 N 个结点(leetcode T19)

考点预览&#xff1a; 双指针法&#xff1a;通过维护两个指针来一次遍历链表&#xff0c;避免了多次遍历链表的低效方法。 边界条件&#xff1a;要特别处理删除头结点的情况。 题目描述&#xff1a; 给你一个链表&#xff0c;删除链表的倒数第 n 个结点&#xff0c;并且返回…

人工智能定义

一、人工智能核心概念体系 1.1 人工智能的本质 人工智能的定义:人工智能(Artificial Intelligence,简称 AI)是指计算机系统能够执行通常需要人类智能才能完成的任务,如学习、推理、解决问题、理解自然语言、识别图像和声音等。它通过模拟人类的智能行为,运用算法和数据…

量子计算的威胁,以及企业可以采取的措施

当谷歌、IBM、Honeywell和微软等科技巨头纷纷投身量子计算领域时&#xff0c;一场技术军备竞赛已然拉开帷幕。 量子计算虽能为全球数字经济带来巨大价值&#xff0c;但也有可能对相互关联的系统、设备和数据造成损害。这一潜在影响在全球网络安全领域引起了强烈关注。也正因如…

0—QT ui界面一览

2025.2.26&#xff0c;感谢gpt4 1.控件盒子 1. Layouts&#xff08;布局&#xff09; 布局控件用于组织界面上的控件&#xff0c;确保它们的位置和排列方式合理。 Vertical Layout&#xff08;垂直布局&#xff09; &#xff1a;将控件按垂直方向排列。 建议&#xff1a;适…

【Uniapp-Vue3】导入uni-id用户体系

在uniapp官网的uniCloud中下载uni-id用户体系 或者直接进入加载&#xff0c;下载地址&#xff1a;uni-id-pages - DCloud 插件市场 进入以后下载插件&#xff0c;打开HbuilderX 选中项目&#xff0c;点击确定 点击跳过 点击合并 右键uniCloud文件夹下的database文件夹&#x…

如何免费使用稳定的deepseek

0、背景&#xff1a; 在AI辅助工作中&#xff0c;除了使用cursor做编程外&#xff0c;使用deepseek R1进行问题分析、数据分析、代码分析效果非常好。现在我经常会去拿行业信息、遇到的问题等去咨询R1&#xff0c;也给了自己不少启示。但是由于官网稳定性很差&#xff0c;很多…

VSCode+PlatformIO报错 找不到头文件

如图示&#xff0c;找不到目标头文件 demo工程运行正常&#xff0c;考虑在src文件夹内开辟自己的代码&#xff0c;添加后没有找到 找了些资料&#xff0c;大概记录如下&#xff1a; 1、c_cpp_properties.json 内记录 头文件配置 .vscode 中&#xff0c;此文件是自动生成的&a…