我是靠谱客的博主 无情鞋垫,最近开发中收集的这篇文章主要介绍原码、补码以及正数/负数的左移和右移,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

文章目录

    • 一 、原码和补码
      • 1.1 原码表示法
      • 1.2 补码表示法
    • 二、C++正数/负数的左移和右移
      • 2.1 正数的左移和右移
      • 2.2 负数的左移和右移
      • 2.3 扩展(对正负数都适用,下面以正数为例)

一 、原码和补码

对数据用n位二进制数编码后,机器数X表示为: X = X n X n − 1 X n − 2 . . . X 2 X 1 X=X_nX_{n-1}X_{n-2}...X_{2}X_{1} X=XnXn1Xn2...X2X1

1.1 原码表示法

1)原码的定义:一个数的原码表示由符号位直接后跟数值位构成,因此,也称“符号-数值 (sign and magnitude)“表示法。原码表示法中,正数和负数的编码表示仅符号位不同,数值部分完全相同。

2)原码编码规则如下:

  • 当X为正数时, X n = 0 X_n=0 Xn=0,后面的位数表示数值。

  • 当X为负数时, X n = 1 X_n=1 Xn=1,后面的位数表示数值。

3)原码0有两种表示形式:

  • [ + 0 ] 原 = 000 … 0 [+0]_原 = 000…0 [+0]=0000

  • [ − 0 ] 原 = 100 … 0 [-0]_原=100…0 [0]=1000

4)原码表示范围(对于n位二进制编码): − ( 2 n − 1 − 1 ) -(2^{n-1}-1) (2n11) ( 2 n − 1 − 1 ) (2^{n-1}-1) (2n11)

1.2 补码表示法

1)补码的定义:正数的补码是它本身;负数的补码等于模与该负数绝对值之差。(对于n位二进制编码,它的模是 2 n 2^n 2n

2)补码0的表示形式:

  • [ + 0 ] 补 = [ − 0 ] 补 = 2 n + ( 士 0 ) = 100 … 0 = 00 … 0 ( m o d 2 n ) [+0]_补=[-0]_补=2^n+(士0)=100…0=00…0(mod 2^n) [+0]=[0]=2n+(0)=1000=000(mod2n)

从上述结果可知,补码0的表示是唯一的。这带来了以下两个方面的好处:一是减少了+0和-0之间的转换。二是少占用一个编码表示,使补码比原码能多表示一个最小负数。

3)求一个数的补码(在原码的基础上):

  • 对于正数,符号位取0,数值部分不变。
  • 对于负数,符号位取1,对数值部分“各位取反,末尾加1”。

4)补码表示范围(对于n位二进制编码): − ( 2 n − 1 ) -(2^{n-1}) (2n1) ( 2 n − 1 − 1 ) (2^{n-1}-1) (2n11)


二、C++正数/负数的左移和右移

在机器中,数的二进制码的表示形式是补码。

2.1 正数的左移和右移

正数的移位比较简单,左移是在二进制数的右边补0,右移是在二进制数的左边补0(因为符号位是0)。

#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
	return 0;
}

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
	return 0;
}

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
	return 0;
}

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
	return 0;
}

2)另外注意,如果移位的位数没有等于或超过该数值类型的最大位数,即使移位相同的位数(通过几次移位),结果也是不一样的,如:

#include <iostream>
using namespace std;
int main() {
	int a = 3;
	a = a << 31;
	cout << (a << 2) << endl;  // 结果为:0
	return 0;
}

这里和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
	return 0;
}

注:移位负数这里我查了一些资料并测试过,但还不能肯定是这样计算,如有错,望指正。

最后

以上就是无情鞋垫为你收集整理的原码、补码以及正数/负数的左移和右移的全部内容,希望文章能够帮你解决原码、补码以及正数/负数的左移和右移所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(35)

评论列表共有 0 条评论

立即
投稿
返回
顶部