斯坦福编程范式第二课笔记-数据类型在内存中的表示

AlgoCraft
• 阅读 1345

内存的最小单位是字节,一个字节等于8位(bit),每一位要么是0要么是1,也就是用二进制来表示。

一个字节在内存中的表示为:

斯坦福编程范式第二课笔记-数据类型在内存中的表示

无符号整数的表示

无符号二进制转成十进制公式:

斯坦福编程范式第二课笔记-数据类型在内存中的表示

  • w:二进制位的长度。
  • i:二进制位从右往左开始的下标,从0开始计数。
  • w-1:由于i是从0开始计数,所以最后一个下标就是w-1。
  • x(i):第i位的值,要么是0要么是1。
  • 2^i:2的第i次幂。

例如:
无符号二进制数10010 按照公式展开就是:

斯坦福编程范式第二课笔记-数据类型在内存中的表示

如果把这个数用1个字节在计算机中存储,内存中就表示为:

斯坦福编程范式第二课笔记-数据类型在内存中的表示

不足8位,左边补0。

1个字节的无符号正式能表示2^8 = 256个不同的数。能表示最大的数是8个二进位全是1的数等于255,也就是求一个公比为2首项是1等比数列前8项和。二进制位求和公式为(2^n) - 1。总结下来一个n位的二进制数能表示最大的数是(2^n) - 1,能够表示2^n个不同的数,之所以是2^n个不同的数,是因为可以表示0~(2^n) - 1,从0开始的所以还需要+1个长度。

Char在内存中的表示

Char类型是用来存储单个字符,在内存中占用1个字节的大小,它使用8个bit来表示256个字符。
Char类型实际存储的是字符的ASCII码,由于ASCII码是整数。所以Char最终在内存中是一个8bit的整型。

比如字符AASCII码是65,65 = 2^0 + 2^6,所以在内存中的表示为:
斯坦福编程范式第二课笔记-数据类型在内存中的表示

char ch = 'A';
printf("%d", ch); // output is 65

Short在内存中的表示

Short 表示的是短整型,一般占用2个字节的内存大小。

它的取值范围是(-2)^15~(2^15)-1包含0。最大值这里是(2^15)-1,是因为short有符号位,需要用最高位(用从左到右第一位)来表示符号,0表示正数,1表示负数。 最大值的二进制表示为0111111111111111(16个二进制位),十进制就是(2^15)-1。 之所以是(2^15)-1,也是之前说的求和公式((2^n)-1

实现加减法

二进制加减法和十进制一样,把对应相加,大于1就向前进位。例如0111 + 1 = 1000

如果想要把7和-7相加使结果等于0。按照在计算机中使用二进制的最高位来当做符号位的,0表示正数,1表示负数。那么7表示为0000111,-7就表示为1000111 。0000111 + 1000111 按照二进制先前的加法法则得出来是1001110,结果不是我们想要的0。

怎么才能让2个二进制数相加得到0呢?

想要得到0,就需要利用进位,比如在11111111(8个1)的基础上加1就可以得到100000000(一共9位,左边第一位是1,后面8个0) ,舍掉最左边的那个1就得到了8个0最终结果就等于0。把原码按位取反然后与原码相加就可以得到全1的二进制数。比如0000111按位取反就是1111000,他们俩相加得到11111111。 再把它加1就得到最后的结果0。整个过程需要3步,我们把最后两步合并成一个步骤,也就是把按位取反和加1合并到一起,其实就是把原码的反码加1。如1111000加1得到1111001。最后这两步合在一起叫做取原码的补码。最后得到的1111001就叫做0000111的补码。

  • 正整数的补码是其本身。
  • 负整数的补码是把它对应的正整数二进制码按位取反,也就做原码的反码然后再加1。

比如正整数7的二进制码是0000111,它的补码还是它本身。再比如-7对应的正整数二进制码是0000111,它的反码就是1111000(把原码按位取反)。然后再加1就得到11110011111001就是-7的补码。我们再次把11110010000111按照二进制加法法则相加刚好得到0。这里需要注意的是,这里左边会产生一个溢出位,这个溢出位是去掉不要的,得到结果就是0。

-1的补码全是1,因为它加上1之后就变成了0。

计算机系统都是用补码来表示二进制码,这样的好处之一就是可以让加减法运算统一处理。

位模式拷贝

当把char类型的变量赋值给short类型的变量时,会把char的8个bit放在short的低八位(从右往左第一个字节)上。

例如:

char ch = 'A'; // 'A' ASCII:65 内存表示为 01000001
short s = ch; // 内存表示为 00000000 | 01000001

一个特殊的情况就是当把一个short-1赋值给一个int变量的时候,并不会得到00000000 | 00000000 | 11111111 | 11111111,因为如果这样的话表示的值就不是-1了。所以正确的做法就是把所有的1全部拷贝给int
例如:

short s = -1; // 内存表示为 11111111 | 11111111
int i = s; // 内存表示为 11111111 | 11111111 | 11111111 | 11111111

相反如果把short类型的变量赋值给char类型的变量时,会把short的低八位(从右往左第一个字节)放在char仅有的一个字节上。会把多的字节自动剔除。
例如:

short s = 65; // 内存表示为 00000001 | 01000001
char ch = s; // 内存表示为 01000001

浮点数的表示

我们已经知道无符号二进制转成十进制公式为:

斯坦福编程范式第二课笔记-数据类型在内存中的表示

这里的i是从0开始的也就是从右边的第一位是2^0,如果我们从一个负整数开始的话,就会存在负整数次幂,那么也就会出现小数部分了。
例如有一个16位的二进制数000000011 | 11000000 用它的前八位来表示整数部分,后八位来表示小数部分,就也可以这样表示000000011.11000000。这样后八位也就不再是整数次幂了,而是从左到右每一位分别是2^(-1)~2^(-8)。这个数就可以表示成:

斯坦福编程范式第二课笔记-数据类型在内存中的表示

这是其中一种浮点数表示方法,这种方法表示的浮点数会出现精度不够,表示的数值区间比较小,所以计算机实际并没有用该方法来表示浮点数。

下面这种方法就是计算机内部真实表示浮点数的方法。

我们先来看下十进制的科学计数法,用科学计数法表示123.45的话就是1.2345 * 10^2。其中1.2345 为尾数,10为基数,2为指数。计算机在表示浮点数的时候,也借用了十进制的科学计数法的思想,只不过基数为2了。

例如1000.01 可以表示成1.00001 * 2^3,几次幂,小数点就向右移动几位。

32位float来举例,首位是符号位S,紧跟后面8位是指数位E,最后23位称为尾数位M

计算公式:

斯坦福编程范式第二课笔记-数据类型在内存中的表示

  • S:符号位
S为0时刚好是正数,为1时是负数。
  • M:尾数部分
它的取值范围是1≤M<2,取值方式是从左到右每一位分别表示的是2^-1~2^-23,值就是然后对各个位的表示值求和,这里跟先前浮点数表示的办法一致,都是从负整数次幂开始。由于尾数的整数部分始终都是1,所以这个1可以被省略,这样就可以多出一位来提升精度。
  • E:指数部分
减去127是因为偏移量是127。

例如0 | 10000010 | 11110000000000000000000的每一部分别是:

  • S:0
表示整数。
  • M:11110000000000000000000
这里需要再加1,因为为了提升小数精度省略了1,所以要加回来。所以完整的尾数部分应该是1.1111(省略了后面的0)。 2^0 + 2^-1 + 2^-2 + 2^-3 + 2^-4 = 1.9375
  • E:10000010
2^7 + 2^1 = 130

分别带入公式得:

二进制形式:

斯坦福编程范式第二课笔记-数据类型在内存中的表示

十进制形式:

斯坦福编程范式第二课笔记-数据类型在内存中的表示

详细过程:
1 * 1.1111 * 2^(130-127) => 1 * 1.1111 * 2^3 => 1 * 1111.1(几次幂,小数点就向右移动几位) => 1 * (2^3 + 2^2 + 2^1 + 2^0 + 2^-1) => 1 * (8 + 4 + 2 + 1 + 0.5) => 15.5

浮点数与整数相互赋值

当我们在把浮点数与整数相互赋值的时候,并不会直接拷贝bit位,而是重新计算出在新的类型中的位模式。
例如:

int i = 5; // 内存表示 00000000 | 00000000 | 00000000 | 00000101
// 重新计算5在float中的表示方式
float f = i; // 内存表示 0 | 00000000 | 00000000000000000000101
printf("%f", f) // output is 5.0

来一点更刺激的!!!

// 2^30
int i = 1073741824; // 内存表示 01000000 | 00000000 | 00000000 | 00000000
// 这里就不会重新计算在float中的表示方式了,而是直接把bit位拷贝过去。用float的解析方式去解析int的那块内存。
float f = *(float *)&i; // 内存表示 0 | 10000000 |00000000000000000000000

// 1 * 2^(128-127) * 1 = 2
printf("%f", f) // output is 2.0

这里就不会重新计算1073741824在float中的表示方式了,而是直接把intbit位拷贝过去。用float的解析方式去解析int的那块内存

点赞
收藏
评论区
推荐文章
blmius blmius
4年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
Wesley13 Wesley13
4年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Jacquelyn38 Jacquelyn38
4年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Wesley13 Wesley13
4年前
FLV文件格式
1.        FLV文件对齐方式FLV文件以大端对齐方式存放多字节整型。如存放数字无符号16位的数字300(0x012C),那么在FLV文件中存放的顺序是:|0x01|0x2C|。如果是无符号32位数字300(0x0000012C),那么在FLV文件中的存放顺序是:|0x00|0x00|0x00|0x01|0x2C。2.  
Wesley13 Wesley13
4年前
mysql中时间比较的实现
MySql中时间比较的实现unix\_timestamp()unix\_timestamp函数可以接受一个参数,也可以不使用参数。它的返回值是一个无符号的整数。不使用参数,它返回自1970年1月1日0时0分0秒到现在所经过的秒数,如果使用参数,参数的类型为时间类型或者时间类型的字符串表示,则是从1970010100:00:0
Wesley13 Wesley13
4年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
4年前
PHP创建多级树型结构
<!lang:php<?php$areaarray(array('id'1,'pid'0,'name''中国'),array('id'5,'pid'0,'name''美国'),array('id'2,'pid'1,'name''吉林'),array('id'4,'pid'2,'n
Stella981 Stella981
4年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Python进阶者 Python进阶者
2年前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这