计算机整数的存储为什么使用补码

计算机整数的存储为什么使用补码
前言:本文首先会介绍机器数、真值和原码、反码、补码的基本概念;之后,说明为什么使用补码;最后,会介绍发生溢出后值的存储情况。

一 基本概念

我们知道,在计算机的世界里,一切都是01二进制串。要计算两个整数的和,首先要把它们存储进计算机里。使用若干字节存储一个整数时,最高位表示整数的正负号(0表示正,1表示负),其余位表示整数的绝对值。

  • 机器数是一个整数在计算机中的存储形式,包括原码、反码和补码
  • 真值是机器数实际表示的值

以8位二进制表示一个整数为例,介绍原码、反码和补码如何转换

原码就是符号位加上真值的绝对值, 即用第一位表示符号, 其余位表示值.
[+1]原 = 0000 0001[-1]原 = 1000 0001

正数的反码同原码,负数的反码是在其原码的基础上, 符号位不变,其余各个位取反
[+1] = [00000001]原 = [00000001]反 [-1] = [10000001]原 = [11111110]反

正数的补码同原码,负数的补码是在反码的基础上加 1
[+1] = [00000001]原 = [00000001]补 [-1] = [10000001]原 = [11111110]反 = [11111111]补

总结:正数的原码、反码、补码相同。负数的反码是在原码的基础上,符号位不变,其余位取反。负数的补码是在反码的基础上加1(注意,这里并没有提及符号位)

二、为什么使用补码

原码、反码、补码本质上就是整数的不同编码形式。就像我们存储字符会使用不同的字符编码(例如ASCII、UTF-8)一样。

对于8位二进制来说,一共可以有2^8 = 256个编码,原码、反码、补码,在表示一个负整数时,编码相同表示的整数是不同的;表示0及正整数时,编码相同表示的整数也是相同的。

如下表所示,原码、反码、补码二进制编码所表示的整数的真值:

二进制编码 原码 反码 补码
0000 0000 0 0 0
0000 0001 1 1 1
0000 0010 2 2 2
0000 0011 3 3 3
0111 1101 125 125 125
0111 1110 126 126 126
0111 1111 127 127 127
1000 0000 -0 -127 -128
1000 0001 -1 -126 -127
1000 0010 -2 -125 -126
1111 1100 -124 -3 -4
1111 1101 -125 -2 -3
1111 1110 -126 -1 -2
1111 1111 -127 -0 -1

首先,对于原码来说,符号位是不参与运算的,必须先比较大小,再提取符号位,在计算,对于人类来说可能是简单的。但是,会导致电路的设计非常复杂。

反码的符号位是参与运算的,例如,
1000 0001表示-126, 1000 0010表示-125
1000 0001 + 1 = 1000 0010
-126 + 1 = -125
把编码看成一个整体,编码+1,对应的表示的值也+1。这一点原码是不成立的。

但是,存在以下问题:
1111 1111 + 1 = 0000 0000
-0 + 1 = + 0 (+号也可以不写,默认是正数)
-0 + 1 = + 0,显然是不符合逻辑的,另外,-0也是没有意义的。

补码的符号位也是可以运算的,同时可以解决以上问题。
1111 1111 + 1 = 0000 0000
-1 + 1 = + 0;

因此,选择使用补码存储整数,符号位参与运算,电路设计更简单。同时,减法也可以使用加一个负数实现,即,可以把减法转换为加法,因此只需要实现加法运算即可。

总结:选择补码的原因,符号位参与运算,可以把减法转换为加法,不存在正负0的问题。

三 溢出后,如何运算实际存储的值为多少

首先,8位二进制数使用补码表示整数的范围【-128, 127】,不属于其中的数都是溢出的。

假设发生溢出的数是x

第一步res = x % 256
第二步res不溢出,就是最终结果
res仍然溢出,且为正数则 res = res -256
res仍然溢出 , 且为负数则 res = res + 256

例如 257,不在范围中257 % 256 = 1;

128,不在范围中, 128 % 256 = 128,仍然溢出则128 - 256 = - 128;
-129 ,不在范围中, -129 % 256 = -129, 仍然溢出则,-129 + 256 = 127;