一、二进制前置知识
1.十进制和二进制的转换
十进制转换二进制----除基取余法
例如:(55)转换为二进制
用55除2,得到27余1,------1
再用27除2,得到13余1,------1
再用13除2,得到6余1,------1
再用6除2,得到3余0,------0
再用3除2,得到1余1,------1
再用1除2,除不了余1。------1
把余数从下往上取,即 110111 = 55
二进制转十进制----位权法
例如:(110111)转换为十进制
二、硬件知识
任何数据都是以二进制存储在计算机中。
根据冯·诺依曼提出的经典计算机体系结构框架,一台计算机由运算器、控制器、存储器、输入和输出设备组成。其中,运算器只有加法运算器。
计算机虽然没办法做减法,但是可以加上这个数的相反数呀。但是二进制我们不能给它加上负号,于是我们需要引入一个符号位,存储在最左边的一位上,0代表正数,1代表负数。例如一个四位二进制数,最左边是符号位。0001,表示他是+1,1001,表示他是-1。
三,原码
终于到正题了,想象你是当年设计计算机的科研人员。你把带符号位的四位二进制数做运算,向全世界展示你精妙绝伦的设计--符号位。
于是你开始了计算----
- 0001+0010=0011 1+2=3
- 1000+0000=1000 (-0)+0=(-0)
- 0101+1010=1111 5+(-2)=-7
你突然发现,正数加正数没有问题,但是后面的测试出现了问题
1:怎么会有两个0呢?
因为1000和0000都表示 零
2:怎么正数加负数会出现问题呢?
因为符号位引起的。
总结一下:
1、原码直观
2、正数相加没问题
3、0有两种表达,运算时需要将-0转换为0,也就是1000转换为0000
四、反码
正数的反码不变。
负数的反码:符号位不变,将原码取反。
例如:
原码000的反码就是0110
然后我们试一试用反码计算一下刚才没有解决的问题
- 0010 + 1101 = 1111 1+(-1)=0 把1111取反码,1000,也就是说两个相反数相加取反码结果没有问题
- 1110 + 1011 = 1001 (-6)+(-3)=(-6)取反得到1110, 这样算出来的是-6,出错了
总结一下:
1、反码在计算相反数相加的时候不会出错(计算的时候会遇到运算位溢出,此时直接忽略高位即可)
2、0还是有两种表达0000和1111
3、容纳数字的范围和原码相同(如上图,原码与反码集合存在映射关系)
4、往往是中间变换量,不会直接用
五、补码
正数的补码不变。
负数的补码等于反码+1。
我们需要注意的是,目前大多数书籍只介绍了补码如何计算,但是都没有讲清楚为何是这样计算,以及这样计算的依据。
笔者在初学的时候也遇到了很多疑惑。关于补码的严格定义,可以自行百度,里面会介绍模与同余数的概念。
在此简单介绍一下,举一个生活中常见的12进制的例子来说明模的概念:
假如当前有一个时钟,指针指向9,如果我要调到12点,我们有两个方法,+3或者-9.
也就是说凡是-9的运算都可以看做是+3的运算。
这个情况下,模是12(mod)
思考到这里时请放缓脚步,慢慢思考。
我们会发现,9+3居然和9-9在某种意义上的效果是一样的。
既然两者效果相同那么一个数a减去一个数b就相当于加上这个数b的同余数。
于是推出模的一般公式:a-b=a-b+mod=a+mod-b
利用这个思想,我们把它带到二进制的世界里(假设是四位的二进制)
我们试图运算0011 - 0010 = 0001,但是我们发现计算机中没有减法器,不能算。
我想便利用上面的思想,减去一个数等于加上一个数的同余数,也就是0011 加上0010的同余数即可
四位二进制的模 10000,那么0010的同余数就是10000-0010=1110,
那么我们就直接0011+1110=10001,但是我们是四位的运算,多出的一位会被直接舍弃,计算机会把多出来的一位放在psw寄存器中,不讨论。
那么至此,我们就可以利用补码计算加法和减法了。
想必你有疑问,怎么求一个数的补码呢?我们经过大量计算发现,补码竟然是原码的反码+1,非常不可思议。
为什么会这样呢?
六、总结与原理
因为负数的反码加上这个负数的绝对值正好等于1111,在加1,就是10000,也就是四位二进数的模,而负数的补码是它的绝对值的同余数,可以通过模减去负数的绝对值得到它的补码,所以负数的补码就是它的反码+1。
参考文献:Programming in C