数的机器码表示——彻底弄清什么是原码、反码、补码、移码


数的机器码表示

目录
  • 数的机器码表示
    • 原码
      • 定点整数
      • 定点小数
      • 原码的计算
    • 反码
      • 定点整数
      • 定点小数
      • 反码的运算
    • 补码
      • 补码的由来
      • 定点整数
      • 定点小数
      • 补码的运算
    • 移码
      • 移码的表示
      • 移码的特点
    • 原码、反码、补码、移码

为了妥善的处理数据运算过程中符号位的问题,于是就产生了把符号位和数值位一起编码起来表示相应的数的各种表示方法。例如我们熟悉的原码、反码、补码、移码等。通常将未经编码的数称为真值,编码后的数称为机器数或者机器码

  • 真值的形式正、负号加某进制数绝对值的形式,即数的实际值。如+3-5

  • 机器数的形式:真值按某种编码方式进行编码后的数值,即真值在机器中的表示,称为机器数,一般可以分为 无符号数和有符号数两种如:X=01011Y=11011

原码

定点整数

若定点整数的原码形式位\(x_n x_{n-1}\cdot\cdot\cdot x_1x_0\),其中\(x_n\)为符号位,则原码表示的定义为:

\[x_{[原]}=\left\{\begin{matrix} x & 0 \leqslant x<2^n \\ 2^n-x=2^n+|x| & -2^n

在上式中,x代表的是真值。

例如,\(x=+7\),化为二进制表示为\(x=+0111\);\(x_{[原]}=0111\)

\(x=-7\),化为二进制表示为\(x=-0111\);\(x_{[原]}=2^3-(-0111)=1000+0111=1111\)

我们可以总结出来:

  • 对于正数\(x=+x_{n-1}...x_1x_0\),它的原码是它自己本身,常常在最高位前面补0,代表它是一个正数。

    • \(x_{[原]}=0x_{n-1}...x_1x_0\)
  • 对于0,根据原码的定义则有两种表示形式:

    • \(+0=+0_{n-1}...0_10_0\)
      • 此时正0的原码为\(+0_{[原]}=00_{n-1}...0_10_0\)
    • \(-0=-0_{n-1}...0_10_0\)
      • 此时负0的原码为\(-0_{[原]}=10_{n-1}...0_10_0\)
  • 对于负数\(x=-x_{n-1}...x_1x_0\),它的原码是在最高位前面补1,代表它是一个负数。

    • \(x_{[原]}=-x_{n-1}...x_1x_0\)

对于一个定点整数原码\(x_nx_{n-1}...x_1x_0\),最高位\(x_n\)代表符号位,用0来表示正数,用1来表示负数。而\(x_{n-1}...x_1x_0\)则代表的是数值位。它的大小就是该原码对应真值的绝对值。

我们很容易求出它的范围\([-(2^n-1),2^n-1]\)

定点小数

假设定点小数的原码形式为\(x_s.x_1x_2...x_n\)(实际上小数点是不存储的),其中\(x_s\)代表符号位。则原码的定义为:

\[x_{[原]}=\left\{\begin{matrix}x & 0 \leqslant x<1 \\ 1-x=1+|x| & -1

在上式中,x代表的是真值。

例如,\(x=+0.875\),化为二进制表示为\(x=+0.111\);\(x_{[原]}=0.111\)

\(x=-0.875\)二进制表示为\(x=-0.111\);\(x_{[原]}=1.000-(-0.111)=1.000+0.111=1.111\)。 我们可以总结出来:

  • 对于正数\(x=+0.x_{n-1}...x_1x_0\),它的原码是它自己本身,常常在最高位前面补0,代表它是一个正数。(注意,前面的0.实际上是不存储的,也就是实际最高位是\(x_{n-1}\))

    • \(x_{[原]}=0.x_{n-1}...x_1x_0\)
  • 对于0,根据原码的定义则有两种表示形式:

    • \(+0=+0.0_{n-1}...0_10_0\)
      • 此时正0的原码为\(+0_{[原]}=0.0_{n-1}...0_10_0\)
    • \(-0=-0.0_{n-1}...0_10_0\)
      • 此时负0的原码为\(-0_{[原]}=1.0_{n-1}...0_10_0\)
  • 对于负数\(x=-0.x_{n-1}...x_1x_0\),它的原码是在最高位前面补1,代表它是一个负数。(注意,前面的0.实际上是不存储的,也就是实际最高位是\(x_{n-1}\))

    • \(x_{[原]}=-1.x_{n-1}...x_1x_0\)

对于一个定点小数原码 \(x_s.x_1x_2...x_n\),最高位 \(x_s\)代表符号位,用0来表示正数,用1来表示负数。而\(x_1x_2...x_n\)则代表的是数值位。它的大小就是该原码对应真值的绝对值。

我们很容易求出它的范围[\(2^{-n}-1,1-2^{-n}\)]

原码的计算

? 原码虽然表示极为简单,但是最大的问题就是加法运算十分复杂

两数相加时,我们需要考虑两个数的符号,如果符号相同,我们可以把数值位直接相加,然后在最高位前边添加上原来的符号位。但如果符号位不相同,那么我们就要考虑进行减法运算。进行减法的时候,我们需要考虑数值位的绝对值大小来决定符号位。这也就是原码运算的缺点:参与运算复杂,需要将数值位与符号位分开考虑。

反码

当进行减法的时候,人们想到如何把减法变为加法,那么这样会更加简便,只靠加法器就可以完成运算。比如

\(1-1=0\)

\(0001-0001=0000\)

\(1+(-1)=0\)

\(0001+1001=1010=-2\)

显然是计算错误的。

于是人们引入了反码来表示负数。

定点整数

设定点整数的反码形式为\(x_n x_{n-1}\cdot\cdot\cdot x_1x_0\)\(x_n\)代表符号位。

\[x_{[反]}=\left\{\begin{matrix}x & 0 \leqslant x<2^n-1 \\ 2^{n}-1+x=2^n-1-|x| & -(2^n-1) < x\leqslant0\end{matrix}\right. \]

对于正数,它的反码为它本身,如+7=0111,它的反码为0111

对于负数,它的反码表示为:符号位不变,数值位取反。,如-7=1000

对于0

  • +0
    • 0000-->0000
  • -0
    • 1000-->1111

定点小数

假设定点小数的反码形式为\(x_s.x_1x_2...x_n\)(实际上小数点是不存储的),其中\(x_s\)代表符号位。则反码的定义为:

\[x_{[反]}=\left\{\begin{matrix}x & 0 \leqslant x<1 \\ 2-2^{-n}+x=2-2^{-n}-|x| & -1

在上式中,x代表的是真值。

反码的运算

注:如果最高位相加后产生进位,则最后得到的结果要加1。

\(1+(-1)=0\)

0001+1110=1111=-0

在以-126+127=1为例

10000001+01111111=100000000

  • 进了一位,末位加一
    • 10000000+01111111+1
    • 最后结果为00000001

也就是说,反码解决了把减法化为加法的问题。

补码

补码的由来

为了解决原码的计算问题,人们引入了补码

我们先从时钟的例子来介绍补码的原理。

比如我们将定在五点的钟表跳到三点,有2种调法:

image-20200318143453928image-20200318143525840

我们既可以顺时针调也可以逆时针调。也就是说我们\(5-2\)\(5+10\)的效果是一样的。

而把这种思想引入到计算机中,不就可以把减法转为加法了吗?

\[5-2=5+10(MOD 12) \\5+(-2)=5+10(MOD 12)\\ -2=10(MOD 12) \]

在上面的式子中,在模为12的情况下,-2的补码就是10。一个负 数用其补码代替,同样可以得到正确的运算结果。

那什么是呢?

计算机中运算器、寄存器、计数器都有一定的位数,不可能容纳无限大的任意数。当运算结果超出实际的最大表示范围, 就会发生溢出,此时所产生的溢出量就是模(module)

可以把模定义为一个计量器的容量

如一个八位计数器0000 0000~1111 1111,它表示的范围就是[0,255]。当计数器表示为1111 1111 时,如果计数器再加一,那么此计数器就会溢出。计数器上的数值会变成0000 0000.而此计数器的溢出量就是256.

假设此计数器表示一个定点小数,它表示的范围就是[0,2-\(2^{-8}\)]。当表示最大的时候,计数器值为1.111 1111计数器加一的时候,那么计数器就会清零,变为0.0000000。那么此此定点小数的溢出量就是2.

从上面我们可以推导出,一个n+1位定点整数\(x_n x_{(n-1)} ... x_2 x_1 x_0\),它的溢出量为\(2^{n+1}\),所以模为\(2^{n+1}\)

任意一个定点小数\(x_s.x_1x_2...x_n\),它的溢出量是2,所以模为2。

在计算机中,机器能表示的数据位数是固定的, 其运算都是有模运算。若运算结果超出了计算机所能表示的数值范围, 则只保留它的小于模的低n+1位的数值,超过n+1 位的高位部分就自动舍弃了。

下面我们来引入补码的定义

定点整数

定点整数的补码形式为\(x_n x_{n-1}\cdot\cdot\cdot x_1x_0\),其中\(x_n\)为符号位,补码表示的定义为:

\[x_{[补]}=\left\{\begin{matrix}x & 0 \leqslant x<2^n \\ 2^{n+1}+x=2^{n+1}-|x| & -2^n \leqslant x\leqslant0\end{matrix}\right. \]

在上式中,x代表的是真值

例如,\(x=+7\),化为二进制表示为\(x=+0111\);\(x_{[补]}=0111\)

\(x=-7\),化为二进制表示为\(x=-0111\);\(x_{[补]}=2^4+(-0111)=10000-0111=1001\)

我们可以总结出来:

  • 对于正数\(x=+x_{n-1}...x_1x_0\),它的补码是它自己本身,常常在最高位前面补0,代表它是一个正数。

  • \(x_{[补]}=0_nx_{n-1}...x_1x_0\)

  • 对于0,根据补码的定义:

    • \(+0=+0_{n-1}...0_10_0\)

      • 此时正0的补码为\(+0_{[补]}=0_n0_{n-1}...0_10_0\)
    • \(-0=-0_{n-1}...0_10_0\)

      • 此时负0的补码为\(-0_{[补]}=(10_n0_{n-1}...0_10_0-0_{n-1}...0_20_10_1)mod(10_n0_{n-1}...0_10_0)=0_n0_{n-1}...0_10_0\)

      由此可见,零的补码是唯一的,没有+0和-0之分。

  • 对于负数\(x=-x_{n-1}...x_1x_0\)

    ? 常常可以通过把数值位按位取反,然后末位加一来计算负数的补码

    • 如求\(-127\)的补码
      • $[-0111 1111]_{[补]}=10000 0000-01111111 $
      • \(10000000=11111111+1\)
      • \([-0111 1111]_{[补]}=11111111-01111111+1\),这一步刚刚说明了上面的计算方法的原理。

    \(-128\)的补码的求法。

    • -128=-1000 0000
    • \([-10000000]_{[补]}=11111111-10000000+1=10000000\)

    其实还有一个更为渐变的补码的求法:从右到左遇到第一个 1 的前面各位取反。也就是从右向左,遇到1之前,还保持原样。遇到1之后,各位取反。以-126为例:\(-126=-01111110\)

    [-01111110]的补码位10000010,10是不变的。而黑体部分则是取反的。

对于补码的取值范围,10000000-11111111——- \(-128\)~\(-1\)

00000000~011111111 ——0~127

推广到n位数,则补码的范围就是[\(-2^{n-1},2^{n-1}-1\)]

定点小数

定点小数的补码形式为\(x_s.x_1x_2...x_n\)(实际上小数点是不存储的),其中\(x_s\)代表符号位。则补码的定义为:

\[x_{[原]}=\left\{\begin{matrix}x & 0 \leqslant x<1 \\ 2+x=2-|x| & -1\leqslant x\leqslant0\end{matrix}\right. \]

在上式中,x代表的是真值。

例如,\(x=+0.875\),化为二进制表示为\(x=+0.111\);\(x_{[补]}=0.111\)

\(x=-0.875\)二进制表示为\(x=-0.111\);\(x_{[补]}=10.000-(0.111)=1.001\)

我们可以总结出来:

  • 对于正数\(x=+0.x_{n-1}...x_1x_0\),它的补码是它自己本身,常常在最高位前面补0,代表它是一个正数。(注意,前面的0.实际上是不存储的,也就是实际最高位是\(x_{n-1}\))

  • \(x_{[原]}=0.x_{n-1}...x_1x_0\)

  • 对于0,根据补码的定义:

    • \(+0=+0.0_{n-1}...0_10_0\)

      • 此时正0的补码为\(+0_{[补]}=0.0_{n-1}...0_10_0\)
    • \(-0=-0.0_{n-1}...0_10_0\)

      • 此时负0的补码为\(-0_{[补]}=(10.0_{n-1}...0_10_0-0.0_{n-1}...0_10_0)mod(10.0_{n-1}...0_10_0)=0.0_{n-1}...0_10_0\)

      也就是说,0的补码是唯一的

  • 对于负数

    按位取反,末位加一,和定点整数一样。虽然我们看起来有一个小数点,但是实际上小数点是不存储的。

定点小数的补码的范围,0000~0111--> [0,0.875]

1000~1111--> [-1,-0.125]

扩展到n位补码

我们很容易求出它的范围$[-1,1-2^{-(n-1)}] $

补码的运算

假设一个二进制整数补码有n+1位,\(x_nx_{n-1}...x_2x_1x_0\),则补码与真值的对应关系可以这么表示:

\(X_{真值}=-2^n\times x_n+\sum_{0}^{n-1}2^ix_n\)

当该数为正整数的时候,\(x_n\)位变为0,\(0x_{n-1}...x_2x_1x_0\),

\(X_{真值}=\sum_{0}^{n-1}2^ix_n\)

当该数为负整数的时候,\(x_n\)位变为1,\(1 x_{n-1}...x_2x_1x_0\),

\(X_{真值}=-2^n+\sum_{0}^{n-1}2^ix_n\)

  • 以-127为例,-127=-01111111
    • 它的补码为10000001
    • 从补码求真值的过程:-10000000+1=-01111111-1+1=-01111111

上面我们说到过,计算机中用原码进行加减运算是十分麻烦的,那么我们来看一下用补码来运算。

\(126-127=0\)

  • 原码

    • \(01111110-01111111\)

    • 我们需要比较数值位的绝对值大小,来决定符号位,1

    • 1111111-1111110=0000001

    • 10000001

      假设把减法看成加法

      • 01111110+(-01111111)=01111110+11111111=100000001
      • 1会溢出,那么得到的结果就是00000001,按原码表示的化,真值为1,显然是错误的。
  • 补码

    • 01111110+10000001=11111111
    • 转换为真值后为-1。正确的
    • 原理是这样的
      • 126-127=-1
      • 126+(-127)=-1
      • 126+129mod256=-1
      • 这里-127等效于129mod256

由此,我们可以看出,补码实际上是可以直接带符号位运算的运算的。

求相反数的补码:由[X]补求[-X]补

带符号位一起取反,然后末位加一

如求我们已知+127的补码求-127的补码

  • 01111111
  • 10000001

移码

移码通常用于表示浮点数的阶码。由于阶码是k位的整数,假设定点整数移码形式为\(e_ke_{k-1}...e_2e_1e_0\)最高位为符号位是。移码的传统定义是:

\([e]_移=2^k+e\)

上式中,e为真值,\(2^k\)为固定的偏移值常数。

与[x]补的区别:符号位相反

真值 补码 移码
-8 1000 0000
-7 1001 0001
-6 1002 0002
…… ...... ......
0 0000 1000
+1 0001 1001
…… ...... .......
+7 0111 1111

移码的表示

移码表示的机器数为数的真值在数轴上向右平移了 固定的偏移值。

如八位移码:

image-20200319141504001

移码的特点

  • 在移码中,最高位为0表示负数,最高位为1表示正数,这与原 码、补码、反码的符号位取值正好相反
  • 移码为全0时所对应的真值最小,为全1时所对应的真值最大! 因此,移码的大小直观地反映了真值的大小,这将有助于两个 浮点数进行阶码大小比较
  • 真值0在移码中的表示形式是唯一的,即:[+0]移= [0]移= 100…00
  • 移码把真值映射到一个正数域,所以可将移码视为无符号数, 直接按无符号数规则比较大小。
  • 同一数值的移码和补码除最高位相反外,其他各位相同。

原码、反码、补码、移码

取值范围的一个比较