1. 进制
1.1 进制的由来
- 进制:是一种进位的方式。x进制,表示逢x进1。
计算机的电子原件的状态:开,关。
那么,我们表达数据的时候,也是按照开,关的状态来表示的。
如果我们表达数据仅仅使用这两种昨天,那么能够表达的数据是比较少的。
而我们常见的数据,英文字母,数字,标点符号,这就很多了。
为了能够表示更多的数据,国际化组织就规定,用8个这样的信号来表示一个数据,并且用1,0表示两种状态,这个数据的单位叫:字节。
由这样的1,0组成的数据就是二进制数据。
- 单位转换
1byte=8bit
1kB=1024byte
1MB=1024kB
1GB=1024MB
1T=1024GB
- 基础补充:
大写B(byte),字节;小写b(bit),比特;
1B=8b,即一个字节等于8个比特位。
1KB=8kb,k表示千,即1千字节等于8千比特。一般来说,计算机中的进位是1024进位的,但是在通信中,为了方便计算,通常用千进位。
(为什么要用1024进位,因为计算机码是以二进制为基础,2的幂数可以反映二进制的位数,因为2的10次幂是1024,最接近1000(1K),方便十进制的估算。)
1B–1024–>1KB–2014->1MB
1.2 进制的表示
- 定义:
- 二进制数
- 每一位使用两个不同数字表示(0,1)
- 低位和高位的关系是:逢2进1
- 各位的权值是2的整数次幂(基数是2)
- 标志:尾部加B
- 例: 101.01B=1×22+0×22+1×20,+0×2−1+1×2−2=5.25 101.01 B = 1 × 2 2 + 0 × 2 2 + 1 × 2 0 , + 0 × 2 − 1 + 1 × 2 − 2 = 5.25
- 八进制数
- 每一位使用八个不同数字表示(0,1,2,3,4,5,6,7)
- 低位和高位的关系是:逢8进1
- 各位的权值是8的整数次幂(基数是8)
- 标志:尾部加Q
- 例: 365.2Q=3×82+6×81+5×80+2×8−1=245.25 365.2 Q = 3 × 8 2 + 6 × 8 1 + 5 × 8 0 + 2 × 8 − 1 = 245.25
- 十六进制
- 每一位使用十六个不同数字表示(0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F)
- 低位和高位的关系是:逢16进1
- 各位的权值是16的整数次幂(基数是16)
- 标志:尾部加H
- 例: F5.4H=15×161+5×160+4×16−1=245.25 F 5.4 H = 15 × 16 1 + 5 × 16 0 + 4 × 16 − 1 = 245.25
- 二进制数
- 第一种表示方式:
(1100101100)2=(1454)8 ( 1100101100 ) 2 = ( 1454 ) 8
(1100101100)2=(32C)16 ( 1100101100 ) 2 = ( 32 C ) 16 - 第二种表示方式(在末尾加字母):
例如:二进制再末尾加B,十进制加D,八进制加Q,十六进制加H.
1.3 进制的转换
1.3.1 R进制转十进制使用按权展开法
其具体操作方式为:将R进制数的每一位数值用 RK R K 形式表示,即幂的底数是R,指数为K,K与该位和小数点之间的距离有关。当该位位于小数点左边,K值是该位和小数点之间数码的个数,而当该位位于小数点右边,K值是负值,其绝对值是该位和小数点之间数码的个数加1。
例如 二进制:
10100.01=1·24+1·22+1·2−2=20.25
10100.01
=
1
·
2
4
+
1
·
2
2
+
1
·
2
−
2
=
20.25
例如 七进制
604.01=6·72+4·70+1·7−2≈298.02
604.01
=
6
·
7
2
+
4
·
7
0
+
1
·
7
−
2
≈
298.02
1.3.2 十进制转R进制
- 如果是整数,直接使用短除法。(例如将94转换为二进制。)
得到结果为1011110
- 如果是浮点数,对整数和小数分开转换;整数部分:除以2取余,小数部分:乘以2取整。(例如29.6875 -> 11101.1011B)
- 注意十进制小数(如0.63)在转换时会出现二进制无穷小数,这时只能取近似值。
1.3.3 二进制与八进制的互换(用三位二进制数一组表示一位)与十六进制数(用四位二进制数一组表示一位)
- 1位八进制数与3位二进制数的对应关系:
八进制数 二进制数 | 八进制数 二进制数 |
---|---|
0 000 | 4 100 |
1 001 | 5 101 |
2 010 | 6 110 |
3 011 | 7 111 |
- 八进制 -> 二进制:把每个八进制数字改写成等值的3位二进制数,且保持高地位的次序不变。
例:2 4 6 3 2 Q - >
010 100 110 111 011 010 B
- 二进制 ->八进制:整数部分从低位到高位每3组用一个等值的八进制数来替换,不足3位时在高位补0凑满3位;小数部分从高位向低位每3位用一个等值八进制数来替换,不足3位时在低位补0凑满三位。
例:1 101 001 110. 110 01B ->
001 101 001 110. 110 010B ->
1 5 1 6 6 2Q
2. 二进制的算术运算
1位二进制数的加,减法运算(定义:在减法算式中,减号前面的数是被减数,减号后面的数是减数,等号后面的数是差.)规则:
2个多位二进制数的加、减法运算举例:
3. 机器数采用原码,反码和补码等编码方法,称为码制。
3.1 机器数与真值
- 3.1 机器数
一个数在计算机中的二进制表示形式, 叫做这个数的机器数。机器数是带符号的,在计算机用一个数的最高位存放符号, 正数为0, 负数为1.
比如,十进制中的数 +3 ,计算机字长为8位,转换成二进制就是00000011。如果是 -3 ,就是 10000011 。
那么,这里的 00000011 和 10000011 就是机器数。 - 3.2 真值
因为第一位是符号位,所以机器数的形式值就不等于真正的数值。例如上面的有符号数 10000011,其最高位1代表负,其真正数值是 -3 而不是形式值131(10000011转换成十进制等于131)。所以,为区别起见,将带符号位的机器数对应的真正数值称为机器数的真值。
例:0000 0001的真值 = +000 0001 = +1,1000 0001的真值 = –000 0001 = –1
3.2 原码,反码和补码
3.2.1 概念
在探求为何机器要使用补码之前, 让我们先了解原码, 反码和补码的概念.对于一个数, 计算机要使用一定的编码方式进行存储. 原码, 反码, 补码是机器存储一个具体数字的编码方式.
- 原码
原码就是符号位加上真值的绝对值, 即用第一位表示符号, 其余位表示值. 比如如果是8位二进制:
[+1]原 = 0000 0001
[-1]原 = 1000 0001
第一位是符号位. 因为第一位是符号位, 所以8位二进制数的取值范围就是:
[1111 1111 , 0111 1111] 即 [-127 , 127]
原码是人脑最容易理解和计算的表示方式.
- 反码
反码的表示方法是:正数的反码是其本身
负数的反码是在其原码的基础上, 符号位不变,其余各个位取反.
[+1] = [00000001]原 = [00000001]反
[-1] = [10000001]原 = [11111110]反
可见如果一个反码表示的是负数, 人脑无法直观的看出来它的数值. 通常要将其转换成原码再计算.
- 补码
补码的表示方法是:
正数的补码就是其本身
负数的补码是在其原码的基础上, 符号位不变, 其余各位取反, 最后+1. (即在反码的基础上+1)
[+1] = [00000001]原 = [00000001]反 = [00000001]补
[-1] = [10000001]原 = [11111110]反 = [11111111]补
对于负数, 补码表示方式也是人脑无法直观看出其数值的. 通常也需要转换成原码在计算其数值.
3.2.2. 为何要使用原码, 反码和补码
现在我们知道了计算机可以有三种编码方式表示一个数. 对于正数因为三种编码方式的结果都相同:
[+1] = [00000001]原 = [00000001]反 = [00000001]补
所以不需要过多解释. 但是对于负数:
[-1] = [10000001]原 = [11111110]反 = [11111111]补
可见原码, 反码和补码是完全不同的. 既然原码才是被人脑直接识别并用于计算表示方式, 为何还会有反码和补码呢?
首先, 因为人脑可以知道第一位是符号位, 在计算的时候我们会根据符号位, 选择对真值区域的加减. (真值的概念在本文最开头). 但是对于计算机, 加减乘数已经是最基础的运算, 要设计的尽量简单. 计算机辨别”符号位”显然会让计算机的基础电路设计变得十分复杂! 于是人们想出了将符号位也参与运算的方法. 我们知道, 根据运算法则减去一个正数等于加上一个负数, 即: 1-1 = 1 + (-1) = 0 , 所以机器可以只有加法而没有减法, 这样计算机运算的设计就更简单了.
于是人们开始探索 将符号位参与运算, 并且只保留加法的方法. 首先来看原码:
计算十进制的表达式: 1-1=0
1 - 1 = 1 + (-1) = [00000001]原 + [10000001]原 = [10000010]原 = -2
如果用原码表示, 让符号位也参与计算, 显然对于减法来说, 结果是不正确的.这也就是为何计算机内部不使用原码表示一个数.
为了解决原码做减法的问题, 出现了反码:
计算十进制的表达式: 1-1=0
1 - 1 = 1 + (-1) = [0000 0001]原 + [1000 0001]原= [0000 0001]反 + [1111 1110]反 = [1111 1111]反 = [1000 0000]原 = -0
发现用反码计算减法, 结果的真值部分是正确的. 而唯一的问题其实就出现在”0”这个特殊的数值上. 虽然人们理解上+0和-0是一样的, 但是0带符号是没有任何意义的. 而且会有[0000 0000]原和[1000 0000]原两个编码表示0.
于是补码的出现, 解决了0的符号以及两个编码的问题:
1-1 = 1 + (-1) = [0000 0001]原 + [1000 0001]原 = [0000 0001]补 + [1111 1111]补 = [0000 0000]补=[0000 0000]原
这样0用[0000 0000]表示, 而以前出现问题的-0则不存在了.而且可以用[1000 0000]表示-128:
(-1) + (-127) = [1000 0001]原 + [1111 1111]原 = [1111 1111]补 + [1000 0001]补 = [1000 0000]补
-1-127的结果应该是-128, 在用补码运算的结果中, [1000 0000]补 就是-128. 但是注意因为实际上是使用以前的-0的补码来表示-128, 所以-128并没有原码和反码表示.(对-128的补码表示[1000 0000]补算出来的原码是[0000 0000]原, 这是不正确的)
使用补码, 不仅仅修复了0的符号以及存在两个编码的问题, 而且还能够多表示一个最低数. 这就是为什么8位二进制, 使用原码或反码表示的范围为[-127, +127], 而使用补码表示的范围为[-128, 127].
因为机器使用补码, 所以对于编程中常用到的32位int类型, 可以表示范围是: [-231, 231-1] 因为第一位表示的是符号位.而使用补码表示时又可以多保存一个最小值.
3.2.3 总结:
- 原码是最符合人脑的,用最高位表示符号位,其余位表示数值位。
- 反码是为了方便带符号的机器数之间运算,让符号位也参与运算。(原理是对原码操作,符号位不变,数值位取反)(注意结合1-1这个例子来理解!)
- 补码是为了解决-0以及[00000000]原和[10000000]原两个编码都表示0这个问题,在反码的基础上加1,就能只用[00000000]来表示0了,[10000000]则表示-128.(原理是对反码基础上加1)(注意结合1-1和-1-127这两个例子来理解!)
3.2.4 补码的真正由来(或者说是数学含义)
加法器
计算机里面,只有加法器,没有减法器,所有的减法运算,都必须用加法进行。
即:减去某个数字(或者说加上某个负数)的运算,都应该研究如何用加法来完成。
模、补数
在日常生活当中,可以看到很多这样的事情:
把某物体左转 90 度,和右转 270 度,在不考虑圈数的条件下,最终的效果是相同的;
把分针倒拨 20 分钟,和正拨 40 分钟,在不考虑时针的条件下,效果也是相同的;
把数字 87,减去 25,和加上 75,在不考虑百位数的条件下,效果也是相同的;
……。
上述几组数字,有这样的关系:
90 + 270 = 360
20 + 40 = 60
25 + 75 = 100
式中的 360、60 和 100,就是“模”(也可以理解成“进制”)。
式中的 90 和 270、20 和 40,以及 25 和 75,就是一对对“互补”的数字。
知道了“模”,求某个数字的“补数”,就是轻而易举的了:
如果模为 365,数字 120 的补数为:365 - 120 = 245。
用补数代替原数,可把减法转变为加法。出现的进位就是模,此时的进位,就应该忽略不计。
二进制数的模
前面说过的十进制数 25 和 75,它们是 2 位数的运算,模是 100,即 1 的后面加上 2 个 0。
如果有 3 位数参加运算,模就是 1000,即 1 的后面加上 3 个 0。
这里的 1000,是十进制数的一千,可以写成 10^3,即 10 的 3 次方。
推论:有多少位数参加运算,模就是在 1 的后面加上多少个 0。
对于二进制数字,模也是这样推算。
如果是 3 位二进制数参加运算,模就是 1000,即 1 的后面加上 3 个 0;
那么当 8 位二进制数参加运算,模就是 1 0000 0000,即 1 的后面加上 8 个 0。
16 位二进制数参加运算,模可就大了,是 1 的后面加上 16 个 0。
注意:这里提到的 1、0,都是二进制数。
8 位二进制数的模可以按照十进制写成 2^8,即 256。
16 位数二进制数的模,就是 2^16,按照十进制,它就是 65536。
二进制数的补码
求二进制数的补数,目的是往计算机里面存放。
在计算机里面,存放的数字什么的,都称为机器码;那么二进制形式的补数,也就改称为补码了。
一般情况下,都是以 8 位二进制数来讨论补码,少数也有用 16 位数的。
计算时加上正数,是不需要进行求取补数的;只有进行减法(或者加上负数),才需要对减数求补数。
补码就是按照这个要求来定义的:正数不变,负数即用模减去绝对值。
已知一个数 X,其 8 位字长的补码定义为:
(1) X ; 0 <= X <= +127 (正数和0的补码,就是该数字本身)
(2) 2^8 -|X| ; -128 <= X < 0 (负数的补码,就是用 1 0000 0000(2的8次方),减去该数字的绝对值)
例如 X = -126,其补码为 1000 0010,计算方法如下:
1 0000 0000
- 0111 1110
-----------
1000 0010
可以看出,按照补码的定义来求补码,概念十分清晰,方法、步骤也是十分简单的。
3.2.5 原码,反码,补码换算
3.2.5.1 第一种:”直接写出来“的方法
记住反码是对原码进行“符号位不变,数值位取反”的操作;补码是对反码进行“加1”的操作。
3.2.5.2 第二种:使用模和补数的关系求出
使用公式直接求出:
已知一个数 X,其 8 位字长的补码定义为:
(1) X ; 0 <= X <= +127 (正数和0的补码,就是该数字本身)
(2) 2^8 -|X| ; -128 <= X < 0 (负数的补码,就是用 1 0000 0000(2的8次方),减去该数字的绝对值)