假设有个10进制的累加器,机械式的
可以表示三位十进制数,即0-999
999+1 就是 000
123 + 877 也是 000
三位就是用3个齿轮结构实现,实现起来不难
但是如何将负数编码进去呢?
这时可以引入补数的概念,
因为 三位十进制累加器,到999就溢出了,变000
可以用补数来表示 负数
比如 -123 就是 1000-123 = 877 (即十进制中的补码)
这时,数的表示范围 正数:0-499 , 负数 -500 ~ -1
正数还是和原来一样
负数
-1 : 999
-2 : 998
-3 : 997
…
-499 : 501
-500 : 500
可以看出规律
负数的补数(补码)就是 其 1000 减去 其取相反数,等价于 999 减去 其相反数+1
这样编码以后就有个好处,
1 . 充分利用的累加器(或者叫加法器)的溢出的特性
2. 可以用加法来实现减法,而且实现也方便
举例子:
比如 2+999 溢出后 为 1, 等价于2-1 (2+(-1)) ,即 2 + (1000-1) -1000
在数学上是等价的,
再比如:
253 + (-173) = 253 + (1000-173) - 1000 = 80
接下来,考虑二进制,即电子(数字电路,逻辑电路)实现的二进制累加器,
原理和机械实现的十进制累加器原理类似,
但是电子的计算速度更快
假设有一个 8 位二进制的累加器 (寄存器)
可以表示 0 ~ 1111 1111
一样的,有溢出, 1111 1111 + 1 = 0000 0000
int 类型 ,也同样采用补码(补数)来编码
正数 :0 ~127 (取一半,能充分利用)
正数和原来一样
-1 : 1111 1111 (即-1 对 256的补数,即 256 - 1)
-2 : 1111 1110
…
-128 1000 0000 (即-128 对 256 的补数,即 256-128)
找到规律
即计算补码的规律, 取反+1
比如 1001 0101 取反 后 为 ,0110 1010
在二进制里,取反操作 等价于 1111 1111 - a
即 ~1001 0101 = 1111 1111 - 1001 0101 = 0110 1010 是等价的
取反 加 1 相当于 1 0000 0000 - a, 即溢出数 - a
反映在累加器里就是 对其求补数
之前晚自习的时候,看了一些垃圾教材和垃圾教科书,说 最高位为 1 表示 负数,也不能说它完全错误,但是有点在误导人,这就是典型的传统中式教育的弊端,不去引导你思考和推理,
不去推导背后的原理。
而是直接给你个好记的结论,直接记就行了。这种是很不正确的,害的是自己。
反观学校老师,也多半是这样,这种就是典型的偷鸡式学习,非常的不提倡。
教材也说了,补码是取反+1,但也不说为甚么,和怎么推导出来的,完全是在瞎讲。
害得我一脸懵逼,真不知道教材怎么写的,估计编写教材的人也是个二溜子。
根据补数来理解,就更好理解了。
推荐看的书籍 《编码》,《深入理解计算机系统》
现在求补码里的最小值和最大值, 根据补数的性质
如果是3位 十进制数,那么是
最小值为 500, 即 -500
最大值为 499 即 499
如果是 二进制 8位
最小值为 1000 0000 即 -128, 即 1<<7
最大值为 0111 1111 即 127 即 (1<<7)-1
32位二进制
最小值 为 1<<31
最大值为 (1<<31)-1
代码如下
#include<iostream>
#include<limits.h>
using namespace std;
int main(){
cout<<INT_MIN<<endl;
cout<<(1<<31)<<endl;
cout<<INT_MAX<<endl;
cout<<(1<<31)-1<<endl;
cout<<1234<<endl;
//默认是 unsigned int
cout<<(int)0xFFFFFFFF<<endl;//-1
cout<<(int)0xFFFFFFFE<<endl;//-2
cout<<(int)0xFFFFFFFD<<endl;//-3
//求相反数,利用补数
cout<<((int)0xFFFFFFFF-1234+1)<<endl;
return 0;
}
输出:
-2147483648
-2147483648
2147483647
2147483647
1234
-1
-2
-3
-1234
-1234