原码,反码,补码,移码

Last Updated: 2023-10-16 13:18:36 Monday

-- TOC --

反码:one's complementary,补码:two's complementary,基本上所有CPU都用补码来表示负整数(since 1965)!原码自不必说,是什么就是什么。移码用在浮点数的指数部分,the biased exponent。

原码

CPU用原码表示正数,0是0,1是1,2是10,3是11......有人说原码的英文是true code,anyway...

反码

必须记住反码的英文是one's complementary

反码的计算为:各位取反!

反码的计算也对应了一个数学公式,计算B的反码:

$$(2^n-1)-B$$

反码在计算机中很少被使用,有时只是作为计算的中间形式。

反码也曾经是计算机表达负数的一个方案,但此方案有两个问题:(1)有+0和-0两种零;(2)实现的计算电路比使用补码多一个计算步骤。

补码

必须记住补码的英文是two's complementary

这个名字的由来:一个数正负数的二进制表达加起来等于\(2^n\)。

补码的计算:各位取反后加1!

补码的计算也对应了一个数学公式,计算B的补码:

$$2^n-B=(2^n-1)-B+1$$

现代CPU基本都使用 two's complementary,即用补码来表示负整数!

假设一个4bit的空间,用补码表示:

number two's complementary
-8 1000(*)
-7 1001
-6 1010
-5 1011
-4 1100
-3 1101
-2 1110
-1 1111
0 0000
1 0001
2 0010
3 0011
4 0100
5 0101
6 0110
7 0111

负数是补码,正数是原码。或者说,正数的补码就是原码。

CPU使用补码的理由很简单:CPU算得快,电路逻辑简单!做减法等同于加上补码,不分正负。

下面的C代码,在little endian的CPU上,把-3的二进制补码打印出来:

$ cat t7.c
#include <stdio.h>

void getbin(char a, char *bins) {
    static int i = 0;
    unsigned char x = 0x80;
    while (x) {
        if (x & a) bins[i++] = '1';
        else bins[i++] = '0';
        x >>= 1;
    }
}

int main(void) {
    int a = -3;  // show binary of -3
    char *p = (char*)&a;
    char bins[32];
    int i;

    getbin(*(p+3), bins);
    getbin(*(p+2), bins);
    getbin(*(p+1), bins);
    getbin(*(p),   bins);

    for (i=0; i<32; ++i) {
        if ((i%8==0) && (i!=0)) printf(" ");
        printf("%c", bins[i]);
    }
    printf("\n");

    return 0;
}
$ ./t7
11111111 11111111 11111111 11111101

学习资料:Two's Complement

比如,2-5=-3

  0010
- 0101
||
\/
  0010
+ 1011 (0101的补码)
---------
  1101 (-3)

比如,2-(-3)=5

  0010
- 1101
||
\/
  0010
+ 0011 (1101的补码)
---------
  0101 (5)

ALU只需要有加法电路即可。

最大负数的坑

最大负数,没有正数与之对应,有一些常见的坑:

移码

为什么要用移码来表示浮点数中的阶码,即指数部分?

因为阶码E可以是正数,也可以是负数,当进行浮点数的加减运算时,必须先对阶,即比较两个数阶码的大小。为了简化比较操作,使操作过程不涉及阶码的符号,对每个阶码都加上一个正的常数,称为偏置常数(bias),使所有阶码都转换为正整数,这样,在对浮点数的阶码进行比较的时候,就变成了对两个正整数进行比较,简化了对比操作,加快了计算速度。

推荐学习:详解IEEE754浮点数

本文链接:https://cs.pynote.net/hd/202112112/

-- EOF --

-- MORE --