深入理解计算机系统(3)

深入理解计算机系统(3)

本文我们主要讲关于数据的的表示方式:原码,反码和补码。

本文在写作过程中,参考了园中的这篇文章《原码,反码,补码详解》,特此声明。

一原码

计算机中是使用二进制来表示数据的,对于C语言这样的强类型语言,每一个数值类型,都有其范围,例如一个int类型,在32位机器上,其表示的范围如下:


最小值


最大值


有符号整数


-2147483648


217483647


无符号整数


0


4294967295

而如果我们定义了一个int类型,给其的赋值,超出了这个范围,则会出现问题。一般编译器会检查这个问题,会出现编译错误。

为了简单,我们以一个char类型(8位)来进行举例:


最小值


最大值


有符号char


-128


127


无符号char


0


255

那么什么是原码呢:原码是二进制的一种表示方式,其规特点是:

无符号数的每一位都是数字位,

有符号数的最高位是符号位,0表示正数,1表示负数,其余各位表示数值。

1.1无符号数的原码

先说无符号的情况,我们选择几个值


十进制


二进制原码


0


00000000


127


01111111


128


10000000


255


11111111

可见最高位为1,表示十进制的数字128,8位全部都是1,表示255。

例如计算两个数字1和1相加:


十进制


无符号二进制原码表示


加数1


1


00000001


加数2


1


00000001


求和


2


00000010

因此,对于一个无符号数,使用原码表示是正常的。

1.2有符号数的原码

对于有符号数,用原码表示负数,要怎么表示呢?

因为是二进制,也就只有0和1两种符号,于是就定义最高位为符号位:最高位为0的原码,表示一个非负数;最高位为1的原码,表示一个负数。


十进制


无符号原码


有符号原码


0


00000000


00000000


127


01111111


01111111


-127


超出范围,无法表示


11111111


255


11111111


超出范围,无法表示

由上面的表格,我们可以发现原码表示的特点:

1对于一个非负数,在不超出其表示范围的前提下,无符号数和有符号数的原码表示形式是一样的;

2对于一个负数(有符号数),在不超出其表示范围的前提下,其最高位为1,而其余各位与无符号数一样。

那么如果我们在进行计算的时候,使用原码来对两个有符号数进行二进制加法运算会怎么样呢?

两个数都非负数,计算结果是是正确的;

如果有一个数是负数,就会出现问题:

例如:计算如下的两个数字,1和-1相加:


十进制


有符号二进制原码表示


加数1


1


00000001


加数2


-1


10000001


求和


-2


10000010

1+(-1) = -2

这显然,不是我们想要的结果。

如果想用原码表示,就需要计算机对负数的原码进行特殊的处理。如上面的例子,加数2的值是10000001,那么最高位不参与计算,只用于标记这是一个负数。然后将一个正数和另一个负数的加法,转变为两个正数的减法。

即将1+(-1)变为1-1,按照二进制的减法运算法则,得到


十进制


有符号二进制原码表示


被减数


1


00000001


减数


1


00000001


求差


0


00000000

1-1=0

这样就得到了,我们想要的结果。

这样看起来似乎也是可以接受的,那我们再算一些其他的数字,例如1+(-2),按照上面的规则,我们要将其变为1-2


十进制


有符号二进制原码表示


被减数


1


00000001


减数


2


00000002


求差


?


?

由于1小于2,因此1减2,等于2减1,再对结果求其相反数。根据减法的规则,1-2= - (2-1)


十进制


有符号二进制原码表示


被减数


2


00000002


减数


1


00000001


求差


1


00000001


求反


-1


10000001

这样我们也得到了,正确的结果。

但是我们要注意以下几点问题:

1要判断运算数中是否有负数

2可能将加法运算变为减法运算

3可能要求其相反数

可见,本来一个简单的加法的运算,由于负数的问题,变得复杂了。而要处理这些问题,计算机需要一些额外存储空间,以及额外的计算步骤。相应的电路的设计就要更加复杂。

结论:复杂度高,不适合。

二反码

如果使用原码可以很方面的进行计算的话,我也就不会使用反码的方式来表示二进制了。

从上一节可以发现,使用原码的方式在做负数的加法时,比较麻烦。

而负数,表示的是一个正数的相反数,那么我们将一个二进制数按位取反,会有怎样的现象呢?

我们看以下这些数字。


原始值


无符号数原码


原码按位取反


去反后的值


0


00000000


11111111


255


127


01111111


10000000


128


255


11111111


00000000


0

可以看出,无符号数,二进制按位取反,其结果为原值被最大值(255)减,即:

0按位取反表示255 – 0 = 255

127按位取反表示 255 – 127 = 128

255按位取反表示 255 – 255 = 0

那么对于有符号数有是什么样呢?我们看以下有符号数字,原码符号位不变,数字位按位取反的计算结果


原始值


有符号数原码


原码按位取反


取反后的值


-127


11111111


10000000


-0


-3


10000011


11111100


-124


-1


10000001


11111110


-126


0


00000000


01111111


127


1


00000001


01111110


126


3


00000011


01111100


124


127


01111111


00000000


0

可以看出,有符号数,除了符号位,二进制按位取反,其结果的绝对值为原值被最大值(127)减,即:

-127按位取反表示- (127 – 127 ) = -0

-3按位取反表示 – (127 – 3 ) = - 124

0按位取反表示 +(127 – 0 )= 127

3按位取反表示+(127 – 3) = 124

127按位取反表示+(127 – 127 )= 0

那么如果我们在计算有符号数加法时,我们可以得到如下的四种情况


使用原码


使用原码取反


正数


负数

我们按照这四种情况分别计算1 + (-1),使用二进制加法,看看有什么有趣的现象

1正数使用机器码,负数使用机器码

在计算符号不同的两个数值的加法时,最终结果的符合与绝对值大的那个相同。

但是如果两个数值不为0,且互为相反数,那最终的结果的符号就不一定了。


十进制


有符号二进制原码表示


加数1


1


00000001


加数2


-1


10000001


求和



x0000010


x=0


2


00000010


X=1


-2


10000010

无论x是正还是负,结果似乎都不正确。

2正数使用机器码,负数使用机器码按位取反


十进制


有符号二进制原码表示


加数1


1


00000001


加数2(按位取反)


-2


11111101


求和


-1


10000001

我们定义最高位符号位的值是x,

对于1 + (-2) ,由于负数的绝对值(2)大于正数的绝对值(1),因此可以确定,x的值是1。

也就是首先能确定最终结果是个负数,然后计算其后面的数值位,

七位的:0000001

加上

七位的:1111101

得到

七位的:1111110

再加上符号位,得到

八位的:11111110

按照前提,这是一个负数,其真值要对其数值为按位取反,得到10000001,即 -1。

这个结果,貌似很好。但是如果计算1+ (-1)会怎么样呢?


十进制


有符号二进制原码表示


加数1


1


00000001


加数2(按位取反)


-1


11111110


求和


??


X1111111


X=0


127


01111111


X=1(按位取反)


-0


10000000

这里在计算00000001和11111110时,由于1和-1的绝对值相等,那么其符号就无法确定。那么我们先不考虑符号位,将数值位进行二进制加法,然后得到x1111111。

那么这时候,x的值有两种可能:

X = 0 ,那这个数字是正数,正数值为:127

X = 1,那这个数字是负数,负数在本环境下是要按位取反来得到其真值的,因此

11111111按位取反,得到10000000,即真值为- 0

那么到底x等于几呢?这个似乎也是个不太好说的问题。

对于 -0 和 127,貌似 -0更加贴近最终的答案,那可以人为的规定,如果负数按位取反,在计算时,符号位优先使用1,即x值无法确定时,优先取1。

我们再计算一下-1 + (-1)


十进制


有符号二进制原码表示


加数1(按位取反)


-1


11111110


加数2(按位取反)


-1


11111110


求和


-124


11111100(溢出)


按位取反


-3


10000011

3正数使用机器码按位取反,负数使用机器码


十进制


有符号二进制原码表示


加数1(按位取反)


2


01111101


加数2


-1


10000001


求和


126


01111110


按位取反


1


00000001


十进制


有符号二进制原码表示


加数1(按位取反)


1


01111110


加数2


-1


10000001


求和


??


X1111111


X=0(按位取反)


0


00000000


X=1


-127


11111111

根据第2中情况的分析,我们可以得到上表的结果。

此时,x的值仍然有两种情况,

X优先取0,则得到结果 0

X优先取1,则得到结果-127

那么这种情况下可以规定,x值优先取0。

我们计算1 + 1


十进制


有符号二进制原码表示


加数1(按位取反)


1


01111110


加数2(按位取反)


1


01111110


求和


124


01111100(溢出)


按位取反


3


00000011

4正数使用机器码取反,负数使用机器码取反


十进制


有符号二进制原码表示


加数1(按位取反)


1


01111110


加数2(按位取反)


-1


11111110


求和


??


X1111100(溢出)


X=0(按位取反)


3


00000011


X=1(按位取反)


-3


10000011

归纳上面的规律


序号


名称


1+1


2+2


1+(-1)


1+(-2)


-1+(-1)


-2+(-2)


1


正原负原码


2


4


2或-2


-3


-2


-4


2


正原负反码


2


4


-0或127


-1


-3


-1


3


正反负原码


3


1


0或-127


-0


-2


-4


4


正反负反码


3


3或-3


-3

可以看到,有价值的是1和2,将两种组合,应该可以计算出正确的结果。


正+正


正+负


负+负


结论


1


Y


N


Y


符号相同成立


2


Y


Y


N


符号不同成立,双正成立

我们使用1和2结合,得到如下二进制加法计算规则:

在符号相同时,符号位不变,数值位直接进行二进制加法。

在符号不同时,符号位由绝对值大的数字的符号决定,如果绝对值相同,优先使用负值,而负数将数值部分,用机器码按位取反后,再与正数进行二进制加法。

那么按照这个规则,在进行计算机的运算处理时,需要考虑哪些问题呢?

1要判断数字的符号

2要根据符号相同和不同采取不同的策略

这里面,1是必须要考虑的,因为有符号数字,必须要有一个位来标识符号。

有了这一点,就可以解决同符号的加法问题了。

对于不同符号的加法,如果正数和负数绝对值相同,仍然是个不好处理的问题。

结论:复杂度低,但是还有待改善

三补码

上节讲的,正数用机器码表示,负数用机器码按位取反的表示方法,就是反码。

而且,一个数字的反码所表示的数字,与自身的和,是固定值——这个数值所能表示的最大值。

对于8位无符号数值类型的变量,其最大值是255。

而7位的话,最大值是127。

因此,对于1+(-2),可以先确定符号位是1,然后计算000 0001和000 0010(取反)的和

即,000 0001 + 111 1101,结果是111 1110。再取反,得到000 0001。再补上最高位的1

得到,1000 0001,结果就是-1。

当正值和负值的绝对值相同的时候,例如 1+(-1),我们规定此时符号0,然后计算

000 0001和000 0001(取反)的和,即000 0001 + 111 1110,结果是111 1111。由于这个数字式正数,因此其值为127。注意,这里的前提是,正数保持原样,负数按位取反。

那么127,代表什么呢?

对于一个char字符,除去其最高位符号位,还剩下7位。

7位二进制的表示范围是000 0000到111 1111,即0到127,共128个数字。

从000 0000开始加1,加127次达到最大值。

达到最大值之后,如果再加1,就得到1 000 0000。而这里的最高位因为溢出而会被舍去,因此只存储了000 0000这7位值,这个值就回到了最初。

对于钟表来讲,从1开始,依次加1,加到12时,达到了最大值。再加1的话,就得到了13,而13由于溢出,因此只保留了1。

因此,我们可以得到这个结果,如果最小值从1开始,那么最大值就是周期。

因此钟表的周期是12。

那么7位二进制数的周期是多少呢?从0到127,为了和钟表统一,我们将其整体加1:

从1开始,最大到128。这样可以推出,7位二进制的周期是128。

也就是说,7位二进制数,表示的数字,具备这样的特性。

数字N与其按位取反的数字M,必有N+M=周期-1。

因此N=周期-1-M。

而周期会溢出,因此在N上增加若干整数倍周期,与没有增加周期,其结果是一样的。

因此有:周期+N = 周期 – 1 – M。注意这里的相等,是去掉溢出位之后相等。

那么所有两边都去掉一个周期。

就得到

N = -1 – M即N=  -(M+1)

因此就有 -N =  M + 1

根据同余的特性,对于任意数字X,X在7位二进制数字表示范围内:

有X+(-N) =  X+ M+1

而M为N的按位取反(反码)。所以对于一个负数,在其二进制反码的基础上在加上数字1,就得到了其同余的数值。这就是现在计算机中实际表示数据的形式——二进制的补码。

以上的推导过程,不是严格的数学证明,只是为了方便理解。

而我们用补码方式来进行计算1 + (-1)

首先确定符号位的值是0

因此计算000 0001 和 000 0001(求补)的和,即

000 0001 + 111 1111 因此,得到000 0000,再补充上最高位0,

得到0 000 0000,即数字0。

完美解决了,在使用反码时,正数情况下的127而负数情况下-0的问题。

结论:使用补码存储数值,可以用加法的运算处理减法。计算相反数的加法,也正确。

可以使用。

四总结

数学是计算机的基础,从计算机的二进制存储可见一斑。因此我们在学习计算机技术的同时,加强数学知识的学习,是非常重要的。

时间: 2024-12-22 09:32:59

深入理解计算机系统(3)的相关文章

深入理解计算机系统9个重点笔记

引言 深入理解计算机系统,对我来说是部大块头.说实话,我没有从头到尾完完整整的全部看完,而是选择性的看了一些我自认为重要的或感兴趣的章节,也从中获益良多,看清楚了计算机系统的一些本质东西或原理性的内容,这对每个想要深入学习编程的程序员来说都是至关重要的.只有很好的理解了系统到底是如何运行我们代码的,我们才能针对系统的特点写出高质量.高效率的代码来.这本书我以后还需要多研究几遍,今天就先总结下书中我已学到的几点知识. 重点笔记 编写高效的程序需要下面几类活动: 选择一组合适的算法和数据结构.这是很

读书笔记《深入理解计算机系统》(第三版) 概述

<深入理解计算机系统>第三版刚出来不到一周,便买下了这本书:之所以阅读本书,一方面源于网友推荐以及豆瓣不错的评分.评价:另一方面是针对本人非科班出身,计算机系统相关的知识相对比较薄弱,很多情况下此类知识需要工作之外的时间自学.补涨此类知识,而该书从程序员的角度进行阐述.展示了计算机系统的各个层面的知识以及其影响程序的正确性.性能.实用性,更容易引起某些上层和下层的知识接轨理解,以期望可以融汇各方面的知识. 明白程序的运作过程,编写更好的软件程序,对底层过程了解以完善自己知识体系和提高信心.接下

《深入理解计算机系统》 Chapter 7 读书笔记

<深入理解计算机系统>Chapter 7 读书笔记 链接是将各种代码和数据部分收集起来并组合成为一个单一文件的过程,这个文件可被加载(货被拷贝)到存储器并执行. 链接的时机 编译时,也就是在源代码被翻译成机器代码时 加载时,也就是在程序被加载器加载到存储器并执行时 运行时,由应用程序执行 链接器使分离编译称为可能. 一.编译器驱动程序 大部分编译系统提供编译驱动程序:代表用户在需要时调用语言预处理器.编译器.汇编器和链接器. 1.将示例程序从ASCⅡ码源文件翻译成可执行目标文件的步骤 (1)运

【转】《深入理解计算机系统》C程序中常见的内存操作有关的典型编程错误

原文地址:http://blog.csdn.net/slvher/article/details/9150597 对C/C++程序员来说,内存管理是个不小的挑战,绝对值得慎之又慎,否则让由上万行代码构成的模块跑起来后才出现内存崩溃,是很让人痛苦的.因为崩溃的位置在时间和空间上,通常是在距真正的错误源一段距离之后才表现出来.前几天线上模块因堆内存写越界1个字节引起各种诡异崩溃,定位问题过程中的折腾仍历历在目,今天读到<深入理解计算机系统>第9章-虚拟存储器,发现书中总结了C程序中常见的内存操作有

《深入理解计算机系统》第六周学习笔记

第四章 处理器体系结构 (一)知识点总结 一.Y86指令集体系结构 1.Y86处理器状态类似于IA32,有8个程序寄存器: %eax.%ecx.%edx.%ebx.%esi.%edi.%esp.%ebp.处理器的每个程序寄存器存储一个字.%esp被入栈.出栈.调用和返回指令作为栈指针. 2.3个一位的条件吗:ZF.SF.OF,它们保存最近的算术或逻辑指令所造成影响的有关信息.程序计数器PC存放当前正在执行指令的地址. 3.程序状态的最后一个部分是状态码stat,它表明程序执行的总体状态 4.Y8

深入理解计算机系统(4.2)---硬件的魅力

引言 这个系列已经很久没更新了,记得上一篇博文已经是三月份了,实在是抱歉.最近业余时间没有以前充裕了,因此更新一篇博文已经变成了一种奢侈.记得以前刚开始写的时候,最多的时候LZ一天写过3篇博文,现在想想,往事如梦. 好了,好不容易写一次,就不多说废话了,本文主要介绍一下硬件以及HCL语言的内容. 从疑问开始 首先,在介绍本文的内容之前,我们先来思考一个看似简单,却实际比较“高深”的问题.众所周知,计算机归根结底是在和0.1打交道,那么到底0和1是如何被计算机记住的呢? 怎么样,这个问题是不是有点

深入理解计算机系统读书笔记一 ---&gt; 计算机基础漫游

一.程序编译的不同阶段. 通常我们是以高级程序开发易于阅读的代码,我们通过语法规则推断代码的具体含义.但是计算机执行代码的时候就需要把代码解析成既定的可执行问题,计算机是如何处理的呢?这里以C语言hello.c文件为例来说明中间过程. #include <stdio.h> int main() { printf("hello world!\n"); } 先上张图. C语言源程序----预处理解析头文件和函数  --- 编译器解析成汇编语言 ---   翻译机器语言指令,打包

20135302魏静静——《深入理解计算机系统》第7章 学习笔记

<深入理解计算机系统>第7章   链接 本章主要内容: 链接——静态链接.动态链接(链接又包括两个主要任务:符号解析和重定位) 符号——全局符号和本地符号.符号表.符号解析 链接文件的创建及引用——gcc.ar rcs.sharedj及fPIC命令参数 重定位——重定位条目.重定位符号引用(PC相对引用和绝对引用) 目标文件——可重定位目标文件(其中又详细介绍了ELF可重定位文件的结构及格式).可执行目标文件.共享目标文件 链接(linking)是将各种代码和数据部分收集起来并组合成为一个单一

深入理解计算机系统(序章)------谈程序员为什么要懂底层计算机结构

万丈高楼平地起,计算机系统就像程序员金字塔的地基.理解了计算机系统的构造原理,在写程序的道路上才能越走越远.道理LZ很早就懂了,可是一直没下定决心好好钻研,或许是觉得日常工作中根本用不到这些,又或许是每次拿起书看到那些复杂的底层架构,看到存储器,寄存器,CPU,总线等等这些概念就头大.总之,由于各种各样的原因,对这块的知识一直没有认真花时间去钻研.那么你可能会问,那你写这篇博客的题目不就是准备学习这方面的知识吗?是的,LZ 准备下定决心钻研了,至于原因如下: ①.经常用一些不知其所以然的技术,会