整数以及浮点数在内存中的存储

一.整数在内存当中的存储

数据在内存中是以十六进制补码的形式进行存储的。

原码表示法简单易懂,适用于乘法,但用原码表示的数进行加减运算比较复杂,当两数相加时,如果同号则数值相加,但是进行减法时要先比较绝对值的大小,然后大数减去小数,最后还要给结果选择恰当的符号。

而负数用补码表示,加法运算只需要一个加法器就可以实现了,不用再配减法器,可以将符号位和数值域统一处理,此外补码与原码相互转换,其运算过程是相同的,不需要额外的硬件电路。

而1个十六进制可以表示4个二进制位,在内存中查看变量时,只用看(32/4)8位二进制代码即可

#include<stdio.h>
int main()
{int hh = 15;return 0;
}

再来看一下负数在内存中的情况

#include<stdio.h>
int main()
{int hh = -15;return 0;
}

此时显示的变量hh在内存中的情况为ff ff ff f1

原码hh=-15为10000000 00000000 00000000 00001111  负数最高符号位为1

           反码为11111111    11111111     11111111    11110000

           补码为11111111     11111111     11111111    11110001

按照一个十六进制位等于4个二进制,将补码可转变为 ff  ff ff f1

大小端字序存储

什么是大小端字序存储

大小端字序存储其实就是字节在内存中存储时的存储顺序。

如果数据的低位字节内容保存在内存的高地址处,而高字节内容保存在内存的低地址处,那么就是大端存储模式

如果数据的高位字节内容保存在内存的高地址处,而低字节内容保存在低地址处,那么就是小端存储模式

比如15,它的十六进制补码为00 00 00 0f

而在vs这个编译器中存储的情况是这样的

编译器里默认左边是低地址,右边是高地址,而存储为00 00 00 00 0f,其实是按照低字节存低地址的规则来进行的,所以是小端存储。

代码判断大小端存储

#include<stdio.h>
int check_sys()
{int i = 1;int ret = (*(char*)&i);//先把i的地址转变为char*,然后解引用会得到内存中存储的十六进制字节序return ret;//char *只能一个字节一个字节访问,此时访问的是第一个内存中的字节
}
int main()
{int ret = check_sys();if (ret == 1)//小端存储低字节存低位{printf("小端\n");}else{printf("大端\n");}return 0;
}

整型提升

#include<stdio.h>
int main()
{unsigned char a = 200;unsigned char b = 100;unsigned char c = 0;c = a + b;printf("%d %d", a + b, c);return 0;}

为什么这代码结果会不一样呢,这就涉及到了整型提升

C语⾔中整型算术运算总是⾄少以缺省整型类型的精度来进⾏的。
为了获得这个精度,表达式中的字符和短整型操作数在使⽤之前被转换为普通整型,这种转换称为整型提升。

整型提升的意义

表达式的整型运算要在CPU的相应运算器件内执⾏,CPU内整型运算器(ALU)的操作数的字节⻓度⼀
般就是int的字节⻓度,同时也是CPU的通⽤寄存器的⻓度。?
因此,即使两个char类型的相加,在CPU执⾏时实际上也要先转换为CPU内整型操作数的标准⻓
度。
通⽤CPU(general-purpose CPU)是难以直接实现两个8⽐特字节直接相加运算(虽然机器指令中可能有这种字节相加指令)。所以,表达式中各种⻓度可能⼩于int⻓度的整型值,都必须先转换为int或unsigned int,然后才能送⼊CPU去执⾏运算。

再回到上面那个代码

c=a+b

a里面放的是200,200的原码本来应该是32位的00000000 00000000 00000000 11001000

 正数原反补码相同都为00000000 00000000 00000000 11001000

放进只有8位的容器char中,发生截断就为1100 1000

b里面放的是100,100的原码本来应该是00000000 00000000 00000000 01100100

 正数原反补码相同都为00000000 00000000 00000000 01100100

放进只有8位的容器char中,发生截断就为0110 0100

b+c直接相加为100101100,这个得到的是补码

此时以%d(32位有符号十进制原码)打印,要先把b+c补码的结果先填满为32位,00000000 00000000 00000001 00101100,然后再转换为原码,此时最高位为0默认为正数,原反补码都相同,所以补码也为00000000 00000000 00000001 00101100,转换为十进制为300,所以最后打印为300

再来考虑c=a+b,八位a和八位b直接相加为100101100,这是九位,要强行放进只有八位的容器c当中,所以会发生截断,最高位1会被截断了。所以c补码最后为00101100

现在要以%d(32位有符号十进制原码)打印,要先把c的补码填满为32位,00000000 00000000 00000000 00101100,正数转原码为000000000 00000000 00000000 00101100,转换为十进制为44,所以结果为44.

有符号的例子

#include<stdio.h>
int main()
{char a;char b = 1;char c = -1;a = b + c;printf("%d %d", b+c, a);
}

结果是一样的,但是先别急,试着用上面的方法来分析一下

-1的原码为10000000 00000000 00000000 00000001

-1的反码为11111111 11111111 11111111 11111110

-1的补码为11111111 11111111 11111111 11111111

1的原码是00000000 00000000 00000000 00000001

这是32位整型的情况,而char类型只能放8位,所以就会发生截断,char c实际存放的是11111111

1是正数,原反补码都一样,都为00000000 00000000 00000000 00000001

这是32位整型的情况,而char类型只能放8位,所以就会发生截断,char b实际存放的是00000001

b+c直接以十进制原码的情况打印出来,所以b和c都要重新填满32位然后相加,这时候要去考虑有符号或者无符号数的情况:无符号数整型提升(填满32位),高位全补0;有符号数用符号位填满剩下的位数。

b为有符号数,补码正数符号位0填满为00000000 00000000 00000000 00000001

c为有符号负数,补码符号位1填满为11111111 11111111 11111111 11111111

补码直接相加为 100000000 00000000 00000000 00000000,是33位,%d打印要求32位整型打印,所以依旧会截断,最高位1截断了,得00000000 00000000 00000000 00000000,此时这得到的是补码,而此时最高位为0,所以是正数,正数原反补码相同,最后十进制打印就为0;

再来考虑一下a=b+c打印的情况,如上面说的char b实际存的是8位00000001,char c存的是8位11111111,直接b+c为100000000,九位数要放进只有8位的char a容器里,直接最高位截断了,即得a=b+c=00000000

 而此时要把a以十进制原码打印出来,a不足32位,所以要填满32位,a为有符号数,最高符号位位为0,补满32位为00000000 00000000 00000000 00000000,最高位为0默认为正数,所以原码十进制为0,最后打印也为0;

无符号有符号整型提升串用的例子

#include<stdio.h>
int main()
{char a = -1;signed char b = -1;unsigned char c = -1;printf("a=%d,b=%d,c=%d", a, b, c);
}

同样的分析方式,a,b为有符号数,c为无符号数,打印结果为有符号十进制整型,%u是打印十进制无符号整型

-1的原码为10000000 00000000 00000000 00000001

-1的反码为11111111 11111111 11111111 11111110

-1的补码为11111111 11111111 11111111 11111111

而char a只能放8位,所以实际char a补码放的是11111111。此时要求输出十二位十进制原码,所以a需要整型提升填满,a是有符号数,此时最高位为1,补码填满为11111111 11111111 11111111 11111111,原码为10000000 00000000 00000000 00000001(符号位不变其余位取反加一),转换成十进制输出就是-1。

b同a一样,也是-1。

而char c补码实际存放的是11111111,c是无符号数,无符号数整型提升填满32位是高位全补0,即为00000000 00000000 00000000 11111111,此时最高位为0,要求打印%d类型的数据,会自动把c的最高位认为符号位,正数原反补码相同,所以原码为00000000 00000000 00000000 11111111,转换成十进制打印就是255.

有符号char和无符号char范围

无符号char最高位为有效位,最高位参与计算,所以范围为0到255(二进制为1111 1111)

也许有人疑惑,1000 0000反码不是1111 1111,加1原码为9位,截断一位应该为1000 0000啊,最高位为-1,结果不应该为-0吗

虽然8位二进制中,存在-0(1000 0000)和0(0000 0000),实际生活中0又没有正负的,-0不也是0吗,但是它却有两种补码表示,。为了将补码与数字一一对应起来,就认为把原码-0的补码强行人为表示为-128,所以八位二进制-128是没有反码和原码的。看见补码1000 0000,不用去按照负数求原码规则取反加一去计算,它直接表示就是-128,这是人为规定的规则

总结有符号char 的范围为-128到127

如果有符号数最大的正数数127(0111 1111)再加上1,会得到-128(1000 0000),而最大的负数-1(1111 1111)再加1其实得到0000 0000,形成一个环形

同样无符号数最大的数255加1就得到最小的数0,其实也是个环形

练习

#include<stdio.h>
int main()
{char a = -128;printf("%u\n", a);return 0;
}

32位-128,原码10000000 00000000 00000000 100000000

反码为11111111 1111111 1111111 01111111

原码为11111111 11111111 11111111 10000000

char a是个8位的容器,32位放进去会截断1000 0000,而接下来要以32无符号整型输出,8位数要重新填满32位,即为11111111 11111111 11111111 10000000,要以无符号数十进制输出,所以最高位1不是符号数,也要参与转换计算。即为4294967168

二.浮点数在内存中的存储

IEEE754标准

根据IEEE(国际电气和电子工程协会)754标准,任意一个浮点数可以写成下面的形式

十进制的浮点数5.0,写成二进制为101.0,相当于1.01 x 2^2

按照上面的格式,可得s=0,m=1.01,E=2;

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

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

浮点数存的过程


IEEE754对有效数字M和指数E,还有⼀些特别规定。
前⾯说过M可以写成 1.xxxxxx 的形式,其中 xxxxxx 表⽰⼩数部分。IEEE754规定,在计算机内部保存M时,默认这个数的第⼀位总是1,因此可以被舍去,只保存后⾯的xxxxxx部分。⽐如保存1.01的时候,只保存01,等到读取的时候,再把第⼀位的1加上去。这样做的⽬的,是节省1位有效数字。以32位浮点数为例,留给M只有23位,将第⼀位的1舍去以后,等于可以保存24位有效数字。
⾄于指数E,情况就⽐较复杂⾸先,E为⼀个⽆符号整数(unsigned int)这意味着,如果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。

举个存储的例子

0.5存储,二进制为0.1,规定正数部分必须为1,所以右移操作得1.0*2^(-1),这是正数所以s=0,

E的存储要加127(1023),-1+127=126,二进制表示为0111 1110.

此时M为1.0,按照规则要舍去1,只保留0,而M要存23位,bu'qi为

浮点数取的过程
 

浮点数取的过的过程可以分为三种情况

E的表示不全为0或者不全为1,这也是最常见的情况

这是浮点数取出E的规则是E的计算值减去127(或者1023),就是把前面存的时候加上的中间数减去,还原原数

E全为0的情况

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

E全为1的情况

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

相关浮点数存储的例题

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

为什么结果会这么奇怪

首先整数5的二进制型式为 00000000 00000000 00000000 00000101

这时候取地址然后强制转换为float *类型,并用浮点数类型指针p去保存它,此时解引用实际上是把上面那一串二进制数以浮点数的形式取出来

而浮点数形式要考虑占8位的E和占23位的M的值

实质就是以下图V的形式进行输出的

5的32位整数二进制形式 00000000 00000000 00000000 00000101

s是二进制序列中的第一位,所以s=0;

E为00000000 ;

M所属的剩下的23位为00000000 00000000 0000101

还原成V的形式,E在32位电脑下要先减去127,所以指数E为-127

而此时E全为0,所以M的1可以不用加

v=(-1)^0 x 0.00000000 00000000 0000101 x 2^(-127)=(-1)^0 x  1.01 x2^(-148)

此时V是非常接近0的数,极限逼近0,所以输出为0.000000

然后第二环节*p=5.0,是直接把n改写成浮点数的形式,此时要以整数的形式把它输出

浮点数5.0的二进制形式为0101.0,换算成科学计数法形式为1.01 x 2^(2)

按上图理解 s=0,M为1.01,E为2

E占8位,且要先+127,所以E表示为100000001

M的1要省略,有效数字为01,要补满23位,所以M为 01000000 00000000 0000000

写成二进制形式S+E+M形式

0 10000001 01000000 00000000 0000000

转换为十进制为1084227584

总的来说,如果一开始n为整数,而现在要用浮点数形式打印出来,实际是浮点数取的规则

如果一开始就为浮点数,那么以32位整数的形式打印出来,实际使用的是浮点数存的规则

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

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

相关文章

认知觉醒(六)

认知觉醒(六) 第二节 感性&#xff1a;顶级的成长竟然是“凭感觉” 人类生存于世&#xff0c;比拼的是脑力思维&#xff0c;但极少有人知道&#xff0c;我们的身体里还有一个更高级的系统&#xff0c;若能善用&#xff0c;成就非凡。 1941年&#xff0c;德军对英国本土进行…

目标检测器技术演进简史

引言 目标检测算法的发展已经取得了长足的进步&#xff0c;从早期的计算机视觉方法开始&#xff0c;通过深度学习达到了很高的准确度。在这篇博文中&#xff0c;我们将一起回顾一下这些算法的发展阶段以及现代目标检测系统中使用的主要方法。 我们首先回顾早期传统的目标检测…

大数据技术3:数据仓库的ETL和分层模型

前言&#xff1a;我们先了解一下数据仓库架构的演变过程。 1 、数据仓库定义 数据仓库是一个面向主题的&#xff08;Subject Oriented&#xff09;、集成的&#xff08;Integrate&#xff09;、相对稳定的&#xff08;Non-Volatile&#xff09;、反映历史变化&#xff08;Time…

电商系统架构演进

聊聊电商系统架构演进 具体以电子商务网站为例&#xff0c; 展示web应用的架构演变过程。 1.0时代 这个时候是一个web项目里包含了所有的模块&#xff0c;一个数据库里包含了所需要的所有表&#xff0c;这时候网站访问量增加时&#xff0c;首先遇到瓶颈的是应用服务器连接数&a…

深入体验:山海鲸可视化软件的独特魅力

山海鲸可视化软件是一款功能强大的数据可视化工具&#xff0c;作为该软件的资深用户&#xff0c;我深感其独特的魅力和优势。下面&#xff0c;我将从软件特点、操作体验、数据交互和实际应用场景等方面&#xff0c;为大家详细介绍山海鲸可视化软件。 首先&#xff0c;山海鲸可视…

解决Eslint和Prettier关于三元运算符的冲突问题

三元运算符Prettier的格式化 三元运算符Eslint的格式要求 解决办法 // eslint加入配置&#xff0c;屏蔽标红报错indent: [error, 2, { ignoredNodes: [ConditionalExpression] }]效果

Nginx按指定格式记录访问日志

今天突然想起来一个日志的一个东西,因为拉项目无意中看到了日志文件的一些东西,现在不经常做后端了,加上其他的一些原因吧.有时候有些问题也没想太多,马马虎虎就过了,后来想想还是要记录一下这方面的处理过程吧: 一般我们作为开发人员关注的日志只是在应用程序层面的,我们称它…

POJ 3735 Training little cats 动态规划(矩阵的幂)

一、题目大意 我们有N只猫&#xff0c;每次循环进行K次操作&#xff08;N<100&#xff0c;K<100&#xff09;&#xff0c;每次操作可有以下三种选择&#xff1a; 1、g i 给第i只猫1个食物 2、e i 让第i只猫吃完它所有的食物 3、s i j 交换第i和j只猫的食物。 求出M次…

销售技巧培训之如何提高手机销售技巧

销售技巧培训之如何提高手机销售技巧 随着科技的迅速发展&#xff0c;手机已成为我们日常生活中不可或缺的一部分。作为一名手机销售员&#xff0c;了解手机销售技巧是必不可少的。本文将通过案例分析与实践&#xff0c;为你揭示手机销售的奥秘。 一、了解客户需求 在销售过程…

AWS Remote Control ( Wi-Fi ) on i.MX RT1060 EVK - 3 “编译 NXP i.MX RT1060”( 完 )

此章节叙述如何修改、建构 i.MX RT1060 的 Sample Code“aws_remote_control_wifi_nxp” 1. 点击“Import SDK example(s)” 2. 选择“MIMXRT1062xxxxA”>“evkmimxrt1060”&#xff0c;并确认 SDK 版本后&#xff0c;点击“Next>” 3. 选择“aws_examples”>“aw…

在 Docker 容器中运行 macOS:接近本机性能,实现高效运行 | 开源日报 No.96

cxli233/FriendsDontLetFriends Stars: 2.6k License: MIT 这个项目是关于数据可视化中好的和不好的实践&#xff0c;作者通过一系列例子解释了哪些图表类型是不合适的&#xff0c;并提供了如何改进或替代它们。主要功能包括展示错误做法以及正确做法&#xff0c;并提供相应代…

【数值计算方法(黄明游)】解线性代数方程组的迭代法(一):向量、矩阵范数与谱半径【理论到程序】

文章目录 一、向量、矩阵范数与谱半径1、向量范数a. 定义及性质补充解释范数差 b. 常见的向量范数 l 1 l_1 l1​、 l 2 l_2 l2​、 l ∞ l_\infty l∞​ 范数性质关系 2、矩阵范数a. 矩阵的范数b. 常见的矩阵范数相容范数算子范数 3、谱半径4、知识点总结1. 向量范数2. 矩阵范数…

Mybatis XML 多表查询

这篇需结合 <<Mybatis XML 配置文件>>那一篇博客一起看 工作中尽量避免使用多表查询,尤其是对性能要求非常高的项目 我们之前建了个用户表(代码在Mybatis XML配置文件那篇博客里),这次再建一个文章表,代码如下 : -- 创建⽂章表 DROP TABLE IF EXISTS articleinf…

vue中组件传值方法

父组件给子组件传值 一、 1.在子组件标签中写入父组件传递数据 向下传递prop 2.在子组件内声明props选项接收父组件传递的数据 props:[,,] 父组件&#xff1a; <Header :msgmsg ></Header> 子组件&#xff1a; props:[msg], 二、 provide i…

AI 训练框架:Pytorch TensorFLow MXNet Caffe ONNX PaddlePaddle

https://medium.com/jit-team/bridge-tools-for-machine-learning-frameworks-3eb68d6c6558

基于jsonrpc4j实现JSON-RPC over HTTP(服务端集成Spring Boot)

1.JSON-RPC说明 JSON-RPC是一个无状态且轻量级的远程过程调用(RPC)协议。 它主要定义了一些数据结构及其相关的处理规则。 它运行时可以基于tcp(socket),http等不同的消息传输方式&#xff0c; 即它不关心底层传输方式的细节。 它使用JSON&#xff08;RFC 4627&#xff09;作为…

Tabbar切换效果(vant)

route 是否开启路由模式 <template><div class"layout-page"><!-- 二级路由出口 --><router-view></router-view><van-tabbar route><van-tabbar-item to"/home">首页<!-- 图标切换为active是高亮 -->&…

JAVA实现敏感词高亮或打码过滤:sensitive-word

练手项目中实现发表文章时检测文章是否带有敏感词&#xff0c;以及对所有敏感词的一键过滤功能 文章目录 效果预览实现步骤 效果预览 随便复制一篇内容到输入框 机器审核文章存在敏感词&#xff0c;弹消息提示并进入人工审核阶段&#xff08;若机器审核通过&#xff0c;则无需审…

eclipse的日志文件放在什么位置

eclipse的日志文件放在<workspace的目录>/.metadata目录下面&#xff0c;例如&#xff1a;

html中一个div中平均一行分配四个盒子,可展开与收起所有的盒子

html中一个div中平均一行分配四个盒子&#xff0c;可展开与收起所有的盒子 1.截图显示部分 2.代码展示部分 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"wid…