今天在阅读关于树状数组和线段树的博客时遇到了一个问题:
对于一个int类型的正整数x,-x=x补。列如x=6,-x=x补=111110102。
看到这里很😵,原来计算机中是用一个数的补码存储它的负数的。
可以用下面的代码验证一下,部分代码参考老王生涯:C++输出二进制数:
#include <iostream>
#include <bitset>
int lowbit(int x);
void printBinary(int x);
int main() {
std::cout<<lowbit(6)<<std::endl;
printBinary(6);
printBinary(-6);
std::cin.get();
return 0;
}
int lowbit(int x) {
return x & -x;
}
void printBinary(int x) {
std::cout<<"Binary of "<<x<<":"<<std::bitset<sizeof(x)*8>(x)<<std::endl;
}
输出:第一行是lowbit函数求一个数二进制位上为1时的最低位,这个不用管。
计算补码的方式:我们都知道计算补码的方式是对这个数的二进制按位取反再加1。
对于补码的解释:这篇博客写得很好liu_runda:补码,我这里稍微修改一下。
对于补码更自然的解释是:对于一个正整数x,如果x是n位的二进制数,则x的补码为2n - x。也就是说x的补码在对2n取模后就是-x。因此,减去一个数就可以看作加上这个数的补码(在对2n取模的意义下),因为这个数的补码就相当于在取模意义下这个数的相反数。
到这里关于补码和负数的知识就介绍完了。
====================================================
有了这些知识后,就可以回答另外一个问题:32位机器int变量的最大值和最小值
。
对于正数的最大值:对于32位有符号int,除去一个最高位符号位后,还剩下31位,可以表示231个数,除去0后还剩下231 - 1个数,可以表示的最大数为232 - 1 = 0x7FFFFFFF。
可以验证一下:
int a = 0x7FFFFFFF;
std::cout<<a<<std::endl;
std::cin.get();
输出:
0111 1111 1111 1111 1111 1111 1111 1111这个二进制数也刚好是2147483647。
对于最小值,前面提到C++中的负数都是由对应的正数的补码存储的。
0的补码很特殊,现在来看一下
对0按位取反为:1111 1111 1111 1111 1111 1111 1111 1111,按位加1后为0000 0000 0000 0000 0000 0000 0000 0000。即0的补码还是0,即-0用0本身表示。
那么此时便可以用1000 0000 0000 0000 0000 0000 0000 0000 这个二进制数表示-2147483648,用代码验证一下:
int a = 0x80000000;
std::cout<<a<<std::endl;
std::cin.get();
输出:
====================================================
总结:
1.在C++中,负数以对应整数的补码形式存储。即对正数的二进制按位取反再加1。这里加号表示连接符。
2.减去一个数等于加上它的补码。
3.补码解释是2n - x。实际计算方式是按位取反再加1。
====================================================
2021/5/24
附上一道leetcode的题:不用加号的加法
做了这道题对计算机中存储负数的方式会有更深的理解