什么是二进制补码
二进制补码是表示负数的一种方法。在计算机中,我们用固定长度的二进制数来存储数字。对于正数,最高位为0,其余位表示数值的大小;而对于负数,最高位为1,其余位同样表示数值的大小。然而,这样表示负数会带来一些问题,比如减法操作需要分别处理不同符号的数。因此,人们设计了二进制补码来进行符号的统一。
原码、反码和补码
原码
原码是最基本的符号表示方法,它的表示方法为:将一个数的绝对值转换成二进制数,并在最高位加上符号位。这种表示方法的优点是简单明了,而缺点也十分明显,在进行计算时几乎都要进行符号分别,不仅在软件实现上效率低下,而且在反向编译等操作下也不是很安全,所以计算机不一般不用这种方式直接存储。
#include<stdio.h>
int main()
{
short n = -6;
// -6在二进制下的原码:
// 1000 0000 0000 0110
printf("%hd\n", n); // -6
return 0;
}
反码
反码就是在表示正数的基础上,将负数的符号位改为1,正数的符号位保持为0。将一个数的反码作为真值与另外一个数(可以为反码或补码)相加,若溢出位为1,则把溢出位回卷到低位。但这种方法仍然不够完美,因为0有两个表示方法:0000和1000,所以依然需要特殊处理。
#include<stdio.h>
int main()
{
short n = -6;
// -6在二进制下的反码:
// 1111 1111 1111 1001
printf("%hd\n", n); // -6
return 0;
}
补码
补码是在反码的基础上,将整个数字加1。这种方法虽然不占用额外的表示范围,但也存在一个问题:最高位是符号位,如果其他位均为0,那么这个数代表的实际意义是-128,而不是0。但由于这个特殊情况发生的概率较小,大多数情况下,补码能够很好地表示数字的正负。
#include<stdio.h>
int main()
{
short n = -6;
// -6在二进制下的补码:
// 1111 1111 1111 1010
printf("%hd\n", n); // -6
return 0;
}
计算补码的方法
直接给出一个数的补码并不困难,只需要将其对应的正数的二进制,按位取反后再加一即可。例如,-6的补码为1111 1111 1111 1010。
但有时候我们需要在程序中动态地计算一个数的补码,这时需要先计算出原码,然后再按照补码的规则进行转换。以下是计算给定二进制数的2的补码算法:
#include <stdio.h>
int main()
{
short x, mask;
scanf("%hd", &x); // 输入一个数的原码
mask = x >> 15; // 求出符号位(正数mask==0,负数mask==-1==0xFFFF)
// 如果是负数,先取反
if (mask == -1) x = ~x;
// 求得补码
x = x + 1;
// 如果是正数,则x-x为0
x = x - (x & (mask));
printf("%hd\n", x);
return 0;
}
以上代码中,我们首先输入一个数的原码,然后利用右移运算符求得原码的符号位,再根据符号位判断这个数是正数还是负数。如果是负数,则将其取反;接着,我们计算出该数的补码;最后,如果是正数,将其-0即可得到补码,如果是负数,需要再进行一次-x的操作来得到补码。
总结
二进制补码是表示负数的一种方法,可以方便地进行符号的统一。计算一个数的二进制补码并不困难,只需要将其对应的正数的二进制,按位取反后再加一即可。在程序中,我们可以利用以下算法来计算一个数的二进制补码:
利用右移运算符求得原码的符号位,再根据符号位判断这个数是正数还是负数;
如果是负数,则将其取反;
计算出该数的补码;
如果是正数,将其-0即可得到补码,如果是负数,需要再进行一次-x的操作来得到补码。
使用该算法,我们可以在程序中方便快速地计算任意二进制数的二进制补码。