原码、补码以及正数负数的左移和右移
⽂章⽬录
⼀ 、原码和补码
对数据⽤n位⼆进制数编码后,机器数X表⽰为:1.1 原码表⽰法
1)原码的定义:⼀个数的原码表⽰由符号位直接后跟数值位构成,因此,也称“符号-数值 (sign and magnitude)“表⽰法。原码表⽰法中,正数和负数的编码表⽰仅符号位不同,数值部分完全相同。2)原码编码规则如下:
当X为正数时,,后⾯的位数表⽰数值。
当X为负数时,,后⾯的位数表⽰数值。3)原码0有两种表⽰形式:
4)原码表⽰范围(对于n位⼆进制编码):到 1.2 补码表⽰法
1)补码的定义:正数的补码是它本⾝;负数的补码等于模与该负数绝对值之差。(对于n位⼆进制编码,它的模是)2)补码0的表⽰形式:
从上述结果可知,补码0的表⽰是唯⼀的。这带来了以下两个⽅⾯的好处:⼀是减少了+0和-0之间的转换。⼆是少占⽤⼀个编码表⽰,使补码⽐原码能多表⽰⼀个最⼩负数。3)求⼀个数的补码(在原码的基础上):对于正数,符号位取0,数值部分不变。
对于负数,符号位取1,对数值部分“各位取反,末尾加1”。
4)补码表⽰范围(对于n位⼆进制编码):到 ⼆、C++正数/负数的左移和右移
在机器中,数的⼆进制码的表⽰形式是补码。
2.1 正数的左移和右移
正数的移位⽐较简单,左移是在⼆进制数的右边补0,右移是在⼆进制数的左边补0(因为符号位是0)。
X =X X X ...X X n n −1n −221
X =n 0X =n 1[+0]=原000 0
[−0]=原100 0
−(2−n −11)(2−n −11)
2n [+0]=补[−0]=补2+n (⼠0)=100…0=00…0(mod 2)
n −(2)n −1(2−n −11)
#include<iostream>
using namespace std;
int main(){
int a =3;// 0000 0000 0000 0000 0000 0000 0000 0011
cout <<(a <<1)<< endl;// 0000 0000 0000 0000 0000 0000 0000 0110:6
cout <<(a >>1)<< endl;// 0000 0000 0000 0000 0000 0000 0000 0001:1
return0;
}
2.2 负数的左移和右移
1)负数的左移:左移是在⼆进制数的右边补0。⼀个负数在左移的过程中会出现有正有负的情况,因为最⾼的符号位可能变成0,所以要清楚负数左移不会特殊处理符号位。如果⼀直左移,最终会变成0。
#include<iostream>
using namespace std;
int main(){
int a =-3;// 1111 1111 1111 1111 1111 1111 1111 1101
cout <<(a <<1)<< endl;// 1111 1111 1111 1111 1111 1111 1111 1010:-6
cout <<(a <<30)<< endl;// 0100 0000 0000 0000 0000 0000 0000 0000:2^30=1073741824
cout <<((a <<30)<<2)<< endl;// 0000 0000 0000 0000 0000 0000 0000 0000:0
return0;
}
2)负数的右移:右移是在⼆进制数的左边补1(因为符号位是1)。如果⼀直右移,最终会变成-1,即⼆进制数变成全1,⽽全1在补码中就表⽰为-1。并且后⾯不管继续右移多少位,结果还是-1。
#include<iostream>
补码的最小负数using namespace std;
int main(){
int a =-3;// 1111 1111 1111 1111 1111 1111 1111 1101
cout <<(a >>1)<< endl;// 1111 1111 1111 1111 1111 1111 1111 1110:-2
cout <<(a >>2)<< endl;// 1111 1111 1111 1111 1111 1111 1111 1111:-1
cout <<(a >>3)<< endl;// 1111 1111 1111 1111 1111 1111 1111 1111:-1
return0;
}
2.3 扩展(对正负数都适⽤,下⾯以正数为例)
1)移位⾥⼀个⽐较特殊的情况是当移位的位数等于或超过该数值类型的最⼤位数时,编译器会⽤移位的位数去模该类型的最⼤位数,然后按余数进⾏移位,如:
#include<iostream>
using namespace std;
int main(){
// 这⾥int类型是32位
int a =3;
cout <<(a <<33)<< endl;// 33 % 32 = 1,左移1位,a变成6
cout <<(a >>33)<< endl;// 33 % 32 = 1,右移1位,a变成1
return0;
}
2)另外注意,如果移位的位数没有等于或超过该数值类型的最⼤位数,即使移位相同的位数(通过⼏次移位),结果也是不⼀样的,如:
#include<iostream>
using namespace std;
int main(){
int a =3;
a = a <<31;
cout <<(a <<2)<< endl;// 结果为:0
return0;
}
这⾥和1)中⼀样总共左移了33位,但结果是0,⽽不是6,要注意这⼀点。
3)移位负数(即移位的位数是负数)
移位负数的计算是,⽤被移动数的数值类型的最⼤位数和该负数相加,再移动所得结果即可。(已测试过,不是⽤负数的数值类型的最⼤位数,⽽是⽤被移动数的)
例如:左移-31,因为a⽤int类型存储,⽽我这⾥int类型是32位存储,所以32+(-31)=1,即左移1位。
#include<iostream>
using namespace std;
int main(){
int a =1;
cout <<(a <<-31)<< endl;// 32+(-31)=1,即左移1位。结果为:2
return0;
}
注:移位负数这⾥我查了⼀些资料并测试过,但还不能肯定是这样计算,如有错,望指正。