计算机整数的存储为什么使用补码
前言:本文首先会介绍机器数、真值和原码、反码、补码的基本概念;之后,说明为什么使用补码;最后,会介绍发生溢出后值的存储情况。
一 基本概念
我们知道,在计算机的世界里,一切都是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;