欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

原码,反码,补码详解及如何计算二进制数中1的数量

程序员文章站 2022-07-15 09:41:56
...

原码、反码、补码的定义

我们都知道计算机无法直接识别十进制数,所以我们要先把十进制数转换成二进制数存在内存中才能进行相应的计算。而原码,反码,补码就是计算机储存一个具体数字的编码方式。

原码:原码就是符号位加上真值的绝对值,即第一位是符号位,其余位表示值。
反码:正数的反码是其本身,负数的反码是在其原码的基础上,符号位不变,其余各个位取反。
补码:正数的补码是它本身,负数的补码是在其原码的基础上,符号位不变,其余各个位取反,最后在加上个1。

为何要使用原码、反码、补码?

现在我们知道了计算机有三种编码方式来表示同一个数,对于正数原码、反码、补码完全相同,而对于负数三者完全不同。既然对于人脑来说,原码是可以被直接理解和识别的编码方式,为何还会出现反码和补码呢?

首先,对于一个二进制数,人脑可以知道第一位是符号位,根据其符号位对真值进行加减。但对于计算机来说辨别一个二进制数的符号位必定会使让计算机的基础电路设计变得十分复杂。所以人们就想出一个办法,让符号位也参与到运算中。我们都知道减去一个正数,相当于加上一个负数,所以在机器中可以只有加法而没有减法,这样计算机的运算就变得更加简单了。

原码计算
那么如果让符号位参与到运算中来,直接使用原码来进行加法计算,结果肯定是不正确的。这也就是为什么在计算机的内部为何不用原码来表示一个数。

反码计算
十进制计算:1-1=0
二进制计算:1-1=1+(-1)= [0000 0001]+[1000 0001]=[0000 0001]+[1111 1110]=[1111 1111]=[1000 0000]=-0

而我们发现用反码来进行运算时,结果的真值部分是正确的,而唯一的问题就出现在0这个特殊值身上。在理解上,-0和+0都是0,有两个编码来表示0分别是[0000 0000]和[1000 0000]。为了解决这个-0的问题补码应运而生。

补码计算
十进制计算:1-1=0
二进制计算:1-1=1+(-1)=[0000 0001]+[1000 0001]=[0000 0001]+[1111 1111]=[0000 0000]=[0000 0000]

从这里我们可以看出,以补码形式在计算时就解决了结果会出现-0的问题。在这里,还有一个需要注意的一点:
(-1)+(-127)=[1000 0001]+[1111 1111]=[1111 1111]+[1000 0001]=[1000 0000]

在这里,补码中的[1000 0000]我们把它当成-128,实际上是把-0的补码来表示-128。ps:补码[1000 0000]的原码算出来是[0000 0000],而[0000 0000]的补码应该是[0000 0000],所以这样计算是不正确的。在补码中-128是没有对映的原码和反码的。

因此,使用补码不仅解决了0的符号和原码和反码中存在的问题,还能多表示一个数字-128。这样我们也可以很好的解释为什么8位二进制数原码和反码的范围均为[-127, 127],而补码的范围为[-128, 127]。而对于我们在编程中使用的32位整型数字,在机器也是用补码表示,所以它的范围可以表示为[-231, 231-1],补码又可以表示一个最小值-231

实例:计算二进制数中1的数量

输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。

刚开始大家可能会想到用1与二进制的每位(通过二进制数的右移)进行比较,如下代码:

//仅试用于正数的解法,对于负数来说可能会陷入死循环
public class Solution {
    public int NumberOf1(int n) {
        int num =0;
        while(n!=0){
        	if(n&1==1){
	        	 num++;
			}
            n=n>>1;
        }
        return num;
    }
}

下面这种方法对上述方法进行改进,对所有二进制整数都适用,:

//此次我们选择对1进行左移来和二进制数的每位进行比较
public class Solution {
    public int NumberOf1(int n) {
        int num =0;
        int one =1;
        while(one!=0){
        	if((n&one)==1){
				num++;
			}
            one=one<<1;
        }
        return num;
    }
}

最优解:

//这个方法十分巧妙
public class Solution {
    public int NumberOf1(int n) {
        int num =0;
        while(n!=0){
            num++;
            n=n&(n-1);
        }
        return num;
    }
}

解析:如果一个整数不为零,那么二进制形式中必然有1。我们把这个整数减一,相当于把最右端的1变成0,而这个1后面的0全都变为了1,在与原数字进行与运算可以消除最后一位1,能够消除几次,则这个二进制整数中所含的1的数量就是多少。直至这个而二进制整数变成零。

举个例子:二进制1110 0000减1等于1101 1111,再与原二进制数进行与运算,可得到1100 0000,1100 0000减1再和1100 0000进行与运算可得1000 0000,继续做相同的运算,我们可知,做三次这样的运算,原数字1110 0000变为0000 0000。因此此方法可以用于计算二进制整数中1的数量。