二进制表示法
在计算机的世界里,只有0和1,我们也通常使用0和1来表示数字,比如8在计算机(8位机)中的二进制表示法就是00001000,那么负数呢?在计算机中如何表示负数呢?
大学学习过的计算机原理中告诉我们负数在计算机中是使用补码(维基百科中也叫作二补数)进行表示的,那么介绍这种表示方法之前想要介绍几个概念。
原码:将一个整数,转换成二进制,就是其原码。如单字节的5的原码为:0000 0101;-5的原码为1000 0101
反码:正数的反码就是其原码;负数的反码是将原码中,除符号位以外,每一位取反。如单字节的5的反码为:0000 0101;-5的原码为1111 1010。
补码:正数的补码就是其原码;负数的反码+1就是补码。如单字节的5的补码为:0000 0101;-5的补码为1111 1011。
1 | System.out.println(Integer.toBinaryString(-5)); |
为什么要使用补码表示负数?
假设我们不使用补码,而仍然采用高位标示符号位的方法,那么-5我们可以表示为1000 0101,那么我们来做一个加法。看看8+(-5)=3是否可以正确实现。
1 | 0 0 0 0 1 0 0 0 |
最终的结果是-13,很明显这个结果并不是我们想要的,难道需要为正数和负数相加设计一套新的电路?
现在我们来看看使用补码的方式,同样运行8+(-5)=3
1 | 0 0 0 0 1 0 0 0 |
此时,得到的就是我们想要的结果3了。
补码的本质
在回答补码为什么能正确实现加法运算之前,我们先看看它的本质,也就是那两个步骤的转换方法是怎么来的。
要将正数转成对应的负数,其实只要用0减去这个数就可以了。比如,-8其实就是0-8。
已知8的二进制是00001000,-8就可以用下面的式子求出:
1 | 0 0 0 0 0 0 0 0 |
因为00000000(被减数)小于0000100(减数),所以不够减。请回忆一下小学算术,如果被减数的某一位小于减数,我们怎么办?很简单,问上一位借1就可以了。
所以,0000000也问上一位借了1,也就是说,被减数其实是100000000,算式也就改写成:
1 | 1 0 0 0 0 0 0 0 0 |
进一步观察,可以发现100000000 = 11111111 + 1,所以上面的式子可以拆成两个:
1 | 1 1 1 1 1 1 1 1 |
2的补码的两个转换步骤就是这么来的。
为什么正数加法适用补码?
实际上,我们要证明的是,X-Y或X+(-Y)可以用X加上Y的2的补码完成。
Y的2的补码等于(11111111-Y)+1。所以,X加上Y的2的补码,就等于X + (11111111-Y) + 1,我们假定这个算式的结果等于Z,即 Z = X + (11111111-Y) + 1
那么Z = X - Y + 100000000 = X-Y(100000000在8位机中产生了溢出),这就证明了可以使用补码来进行负数的加法运算