- 常见数值的补码
数值
补码
0
0000 0000
1
0000 0001
-1
1111 1111
-256
1000 0000
255
0111 1111
最高位是符号位,0表示正数,1表示负数。
- 已知负数求补码
方法:将对应绝对值的补码按位取反,然后加上1,比如求-1的补码,先取1的补码:
0000 0001 1 按位取反 1111 1110 加上1 1111 1111 -1 - 已知负数的补码,求负数
方法:补码-1,然后按位取反得到绝对值,然后加上负号
也可以直接按位取反,得到绝对值加上1,然后加上负号1111 1111 减去1 1111 1110 按位取反 0000 0001 1 加上负号 -1
再如:1111 1111 按位取反 0000 0000 0 加上1 1 填上负号 -1
1000 0000 按位取反 0111 1111 127 加上1 128 填上负号 -128 - 最大值和最小值的特征
从以上补码,可以看出,任何一种数据类型,其最大值的特点是最高位为0,其余为均为1,而最小值,其特点是最高位为1,其余位均为0。因此对于任何一种数据类型,只要通过位移运算,得到符合上述特点的补码就可以获取到相应数据类型的最大值和最小值。现假设要计算短整型的数据范围,则其最大值和最小值的补码如下:255 0111 1111 -256 1000 0000 32768 0111 1111 1111 1111 -32769 1000 0000 0000 0000
要获得以上补码,我们可以看到,最简单的是获得最小值的补码,它可以通过对1左移15位获得,而如果获得了最小值的补码,那么按位取反,就可以获得最大值的补码,过程如下:最大值 0111 1111 1111 1111 最小值 1000 0000 0000 0000
0000 0000 0000 0001 1 左移15位 1000 0000 0000 0000 -32769 按位取反 0111 1111 1111 1111 32768
下面是实验:
1<<(sizeof(int)*8-1) -2147483648 ~(1<<(sizeof(int)*8-1)) 2147483647 注意以下差异: (short)(1<<(sizeof(short)*8-1)) -32768 (short)(~(1<<(sizeof(short)*8-1))) 32767 ((short)1)<<(sizeof(short)*8-1) 32768 ~(((short)1)<<(sizeof(short)*8-1)) -32769 ((int)1)<<(sizeof(int)*8-1) -2147483648 ~(((int)1)<<(sizeof(int)*8-1)) 2147483647 ((long)1)<<(sizeof(long)*8-1) -2147483648 ~(((long)1)<<(sizeof(long)*8-1)) 2147483647
于是,对于一个对输入的整形数乘以一定倍数的模板函数,可以通过下面的方式确定数值范围:
template<typename T> int Test(T x,int nScale) { T nMin = (T)(1<<(sizeof(x)*8-1)); //确定该类型的最大值 T nMax = (T)(~(1<<(sizeof(x)*8-1))); //确定该类型的最小值 int nVal = x*nScale; //乘以一定的倍数 if(nVal<nMin) return nMin; //如果越下界,则返回下界值 else if(nVal>nMax) return nMax; //如果越上界,则返回上界值 else return nVal; //没有越界,返回乘倍后的值 }