概念知识
昨天参加模拟考试,看到这种题目SB了。这里记录一下。
首先,计算中存的数字都是以补码的形式存。
正数和0的补码就是二进制表示本身。负数的补码为对应正数按位取反加1。
比如:
10 的 原码,补码均为:
00000000 00000000 00000000 00001010
-10的补码为10按位取反 + 1
00000000 00000000 00000000 00001010
按位取反
11111111 11111111 11111111 11110101
再 +1
11111111 11111111 11111111 11110110
上面就是-10的补码,也就是计算机中对-10的表示。
另外,如果对-10的补码按位取反 + 1。就能得到对应正数10的补码。
接下来推而广之。
我们知道int的取值范围是 -2^31 到 2 ^ 31 - 1
那么为什么负数会比正数多1呢?其实这和补码的存储有关系。
为了方便我们把整个int定义为下面的部分,计算机中存储的就是下面的东东:
-2^31 | -2^31 + 1 | -1 | 0 | 1 | 2^31 - 1 |
---|---|---|---|---|---|
1000 … 0000 | 1000 … 0001 | 1111 … 1111 | 0000 … 0000 | 0000 … 0001 | 0111 … 1111 |
32位最多可以表示的状态数为 2^32。上面的状态总和即为 2^32。正数部分取反+1后,都有对应的负数。而0和负数最小值取反+1仍为其本身。
其实可以把上图看成环形,当正数最大值+1后,就会到负数最小值。往复不止。
C++中的位运算
& (位 “与”) and
^ (位 “异或”)不同为1,相同为0
| (位 “或”) or
~ (位 “取反”)
<<(左移)
>>(右移)
int main ()
{
//-2^31 -2^31 + 1 -1 0 1 2^31 - 1
//1000 … 0000 1000 … 0001 1111 … 1111 0000 … 0000 0000 … 0001 0111 … 1111
cout << INT_MAX << endl; // 2147483647 0111 ... 1111
cout << (INT_MAX << 1) << endl; // -2 1111 ... 1110 最高位干掉, 最低位补0,在计算机中,这个二进制补码表示的数为 -2, 没问题。
cout << (INT_MAX >> 1) << endl; // 1073741823 右移会把最低位干掉。最高位补0。相当于 (INT_MAX - 1) / 2
cout << INT_MIN << endl; // -2147483648 1000 ... 0000
cout << (INT_MIN << 1) << endl; // 0
cout << (INT_MIN >> 1) << endl; // -1073741824 负数右移会把最低位干掉,最高位补1,这一点需要注意
cout << (~INT_MIN) << endl; // 2147483647 1000 ... 0000 变为 0111 ... 1111 符合预期,确实是int最大值
cout << (~INT_MAX) << endl; // -2147483648
cout << (~0) << endl; // -1 没问题
cout << (INT_MAX | INT_MIN) << endl; // -1
cout << (1 | INT_MIN) << endl; // -2147483647 0000...0001 | 1000...0000 = 1000...0001
cout << (INT_MIN ^ INT_MAX) << endl; // -1
cout << (INT_MIN & INT_MAX) << endl; // 0
}