目录
1.机器数和机器数真值
1.1机器数
机器数是指计算机内部用来表示数值的二进制数。机器数通常包括符号位和数值位,其中符号位用于表示正负,数值位用于表示数的大小。计算机处理和存储数据时,使用的是机器数。
示例: 对于十进制数 +5 和 -5,其机器数表示如下(假设使用8位二进制表示):
- +5:0000 0101
- -5:1000 0101
1.2机器数的真值
机器数的真值是指该机器数在实际应用中的十进制值。真值可以通过机器数的符号位和数值位来确定。符号位为0表示正数,符号位为1表示负数。
示例:
- 机器数 0000 0101 的真值是 +5
- 机器数 1000 0101 的真值是 -5
2.原码,反码,补码的计算方法
原码,反码,补码等都是对机器数的存储形式,也即表示方法。
- 机器数是一种广义的概念,指计算机中用二进制表示的数值。
- 原码是一种具体的表示方法,用最高位表示符号,其余位表示数值。
- 反码用于负数表示,负数的反码是其原码的数值位逐位取反。
- 补码在计算机中广泛使用,负数的补码是其原码的数值位逐位取反后加1。
速记:
2.1原码
原码(Sign-Magnitude)是一种表示有符号数的方法。原码的最高位(符号位)表示数值的正负,其余位表示数值的大小。符号位为0表示正数,符号位为1表示负数。
示例: 对于十进制数 +5 和 -5,其原码表示如下(假设使用8位二进制表示):
- +5:0000 0101
- -5:1000 0101
2.2反码
反码(Ones' Complement)是一种表示负数的方法。正数的反码与其原码相同;负数的反码是将其原码的数值位逐位取反(即0变1,1变0),符号位保持不变。
示例: 对于十进制数 +5 和 -5,其反码表示如下(假设使用8位二进制表示):
- +5:0000 0101
- -5:1111 1010(先取原码1000 0101,然后数值位逐位取反)
2.3补码
补码(Two's Complement)是计算机中广泛使用的一种表示有符号数的方法。正数的补码与其原码相同;负数的补码是将其原码的数值位逐位取反后加1。补码的优点是加减运算可以统一处理,简化了计算过程。
示例: 对于十进制数 +5 和 -5,其补码表示如下(假设使用8位二进制表示):
- +5:0000 0101
- -5:1111 1011(先取原码1000 0101,然后数值位逐位取反得到1111 1010,再加1得到1111 1011)
3.为什么要使用反码和补码?
3.1原码不能让符号位参与运算的问题:
现在我们知道了计算机可以有三种编码方式表示一个数,对于正数因为三种编码方式的结果都相同:
|--------|----------------|----------------|----------------|
| 真值 | 原码 | 反码 | 补码 |
| [+1] | [0000 0001]原 | [0000 0001]反 | [0000 0001]补 |
所以不需要过多解释,但是对于负数:
|--------|----------------|----------------|----------------|
| 真值 | 原码 | 反码 | 补码 |
| [-1] | [1000 0001]原 | [1111 1110]反 | [1111 1111]补 |
可见负数的原码,反码和补码是完全不同的。既然原码才是被人脑直接识别并用于计算表示方式,为何还会有反码和补码呢?
首先, 因为人脑可以知道第一位是符号位,在计算的时候我们会根据符号位,选择对真值区域的加减。(真值的概念在本文最开头)
但是对于计算机,要设计的尽量简单,计算机辨别"符号位"显然会让计算机的基础电路设计变得十分复杂!
于是人们想出了将符号位也参与运算的方法。我们知道,根据运算法则减去一个正数等于加上一个负数,即:1-1 = 1 + (-1) = 0, 所以机器可以只有加法而没有减法,这样计算机运算的设计就更简单了。
于是人们开始探索将符号位参与运算,并且只保留加法的方法。
首先来看原码:
计算十进制的表达式: 1 - 1 = 0
1 - 1 = 1 + (-1) = [0000 0001]原+ [1000 0001]原= [1000 0010]原= -2
如果用原码表示,让符号位也参与计算,显然对于减法来说,结果是不正确的。这也就是为何计算机内部不使用原码表示一个数。
3.2为了解决原码作减法,引入反码
计算十进制的表达式:1 - 1 = 0
bash
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。
3.3为了解决0的符号问题,引入补码
bash
1-1
= 1 + (-1)
= [0000 0001]原+ [1000 0001]原
= [0000 0001]补+ [1111 1111]补
= [1 0000 0000]补 【注意:进位1不在计算机字长里。】
= [0000 0000]补
= [0000 0000]原
= 0
这样0用[0000 0000]表示,而以前出现问题的-0则不存在了。而且可以用[1000 0000]表示-128:-128的由来如下:
bash
(-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] 因为第一位表示的是符号位,而使用补码表示时又可以多保存一个最小值。
4.深入反码和补码背后的意义
4.1钟表的例子
假设需要将钟表从六点调到三点:有两种方式
1.时针逆时针往前拨三格;
2.时针顺时针往后拨九格。
钟表就是一个会回绕的一位十二进制的计算系统:
从1数到12后又会从1开始从头数到12。
如上我们想计算 6-3 = 3的时候可以通过 6 + (12-3) = 3而得出结果;
我们说在这个计算系统中3和9是关于12互补的,也即是3关于12的补数是9,在计算
一个数减去一个数时,可以通过一个数加上这个数的补数来同样得出结果。
这种现象同样在二级制的N位计算机系统中存在。
4.2反码的定义:
反码(Ones' Complement)的本质
英文名字:Ones' Complement ,对一个数关于 "1" 进行求补码。这个 "1" 本质上是一个有限位计数系统里所能表示的最大值。以下是具体解释:
有限位计数系统中的最大值
在不同的进制和位数的系统中,最大值不同:
- 8 位二进制中的最大值是
1111 1111
(255)。 - 1 位十进制中的最大值是
9
。 - 3 位十六进制中的最大值是
FFF
(4095)。
反码的性质
反码的构造方式使得以下性质成立:
- 一个正数的反码与其原码相同。
- 一个负数的反码加上它的原码的绝对值等于 2^n-1(也就是该计算系统中能表示的最大值)。
数学表示
反码的计算可以表示为:
bash
[x]反 = ( M − |x| )
其中,M
是系统中的最大值。例如,在 8 位二进制系统中,M = 255
(即 1111 1111
)。
关于最大值对一个数求补
反码实际上是通过对最大值减去一个数来得到:
- 在 8 位二进制中,最大值是
1111 1111
。对一个数x
求反码就是用1111 1111
减去x的绝对值
。 - 结果体现的是将
x
的数值位每一位取反,即0
变1
,1
变0
。
按位取反
这种减法操作在二进制系统中直接体现为按位取反:
- 例如,
x = 0000 0101
(十进制的 5) - 反码为
1111 1010
(十进制的 250,即255 - 5
)
总的来说,在开头讲的反码的计算方法只是求一个二进制数的反码在具体计算时的具体体现而已。 而这种减法得出的结果是将原码的数值位按位取反的体现也是反码名字的由来。
4.3补码的定义:
英文名字: Two's Complement,对一个数关于"二"求补,这个二指的是一个N位计算系统的容量也即,也叫做模。
计数系统的容量(模)
计数系统的容量(模)是指系统所能表示的所有不同状态的总数。例如:
- 对于 4 位二进制数(不考虑符号),其容量是 2^4 = 16。
- 对于 1 位符号位和 4 位数值位,总共 5 位二进制数,容量是 2^5 = 32。
补码的性质
补码的构造方式使得以下性质成立:
- 一个正数的补码与其原码相同。
- 一个负数的补码加上它的原码的绝对值等于 2^n(也就是该计算系统的模)。
补码的计算公式
补码的计算公式为:
bash
[x]补 = (模 - |x|)
其中,模是计数系统的容量。例如,对于 4 位二进制数,模是 16
为什么补码可以让有符号数直接做加法
补码的一个核心特点是,它将减法运算转换成了加法运算。具体来说,对于一个 n 位二进制数系统,给定两个有符号整数 x 和 y,x - y 可以通过 x + (-y) 来实现,其中 -y 是 y 的补码。
补码系统中的"回绕"
在补码系统中,4 位二进制数的范围是从 0000 到 1111。当结果超过 1111(或小于 0000)时,它会自动回绕。例如:
- 1111 (补码 -1) 加 1 得到 0000(补码 0),这就像钟表从 11 点走到 12 点,然后重新开始。
- 0000 (补码 0) 减 1 得到 1111(补码 -1),这就像钟表从 12 点走到 11 点。
下面通过一个四位二进制计算系统来举例:
|------|----------|
| 所有码值 | 作为补码代表的数 |
| 0000 | 0 |
| 0001 | 1 |
| 0010 | 2 |
| 0011 | 3 |
| 0100 | 4 |
| 0101 | 5 |
| 0110 | 6 |
| 0111 | 7 |
| 1000 | -8 |
| 1001 | -7 |
| 1010 | -6 |
| 1011 | -5 |
| 1100 | -4 |
| 1101 | -3 |
| 1110 | -2 |
| 1111 | -1 |
该计算系统可以类比于一个会回绕的钟表:
假如现在要计算4-2的值:
4 - 2 = 4 + (-2) = 2
减去一个数等于加上这个数的补码:
[0100]补 + [1110]补 =[10010]补
溢出一位,我们不管,系统自动回绕:
[0010]补表示的就是2,可见结果正确。
该过程在图上可以表示为:
而像这种为了弥补反码中零的两种表示方式的漏洞,正是补码名字的由来。
4.4反码和补码计算方法的由来
总体来讲,反码和补码都是利用一个计算系统在不断的累加之后会出现回绕的现象用来正确处理加法的溢位,这样只要不超出该计算系统所表是数值的范围的运算就可以通过这个计算系统来计算相应的加减法。
在一个n位的二进制计算系统中;
对于求反码:
bash
[x]反 = (2^n - 1) - |x|
具体计算时的体现就是数值位按位取反;
对于求补码:
bash
[x]补 = 2^n - |x|
联立二式,就可以推出,补码是原码数值位按位取反后的结果加上1,同时也是反码加1。