欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

二进制中关于负数进行移位运算之后会不会改变符号的探究

程序员文章站 2022-07-15 07:52:04
...

移位运算,即:将二进制数向左或向右移动相应位数,为保证数据位数,空缺部分会进行相应的补齐。

毫无疑问,如果该数是正数,那么无论是向左移位或向右移位,
空缺位都应用0补齐,那么对于负数呢?我们知道负数的高位是用1来表示的,那么移位之后会不会以0补齐从而变为正数?
下面进行测试:
右移:

#include <stdio.h>

int main(){
	short a = -15;
	a = a>>3;
	printf("%d\n",a); //结果为-2
	return 0;
}

我们来通过二进制码来观察一下:


-15:
 原:  		0000 0000 0000 1111
 反:  		1111 1111 1111 0000
 补:  		1111 1111 1111 0001
 右移三位:      ???1 1111 1111 1110		1.

此时我们已知结果为-2,所以:
 原:     	0000 0000 0000 0010
 反:   		1111 1111 1111 1101
 补: 		1111 1111 1111 1110 		2.

显然,2式就是-15向右移三位之后的结果,说明负数右移之后,空白处全都以1补全,所以负数向右移位之后不会改变符号。

那么负数左移呢?会不会出现最高位变成0的情况呢?我们以同样的方法来观察,代码如下:
(为了使得补码最高位在向左移位之后变为0,我们需要一个大一些的数,或者向左移动更多位数,这里作者选择移动更多位数。)

#include <stdio.h>
int main(){
	short a = -9;
	a = a<<12;
	printf("%d\n",a);
	return 0; //结果为28672!!!!惊不惊讶,别慌下结论
}

二进制如下:

-8:
原码:		0000 0000 0000 1001
反码:		1111 1111 1111 0110
补码:		1111 1111 1111 0111
左移12位之后:	0111 0000 0000 0000

好像很有道理的样子,为什么会这样呢?

我们知道,当我们进行向左移位的时候,相当于是乘上2n,n为移动的位数。那么我们将-9*212等于多少呢?-36864,而我们声明的数据类型是short,该数据类型表示的数的范围是-32768-----32767。

显然,我们的结果超出了short类型的范围。

若是将这个数的范围扩大至4byte,那么-9向左移12位之后,结果就会是-36864了。

因此,我们并不能得出,负数向左移位可能会出现变正数的结论。

但是,当我们将数据扩大到4byte之后,将-9向左移动12位之后,显然最高位还是1,并没有变为0。

若我们能证明,对任意一个负整数,向左移动最少位数之后,使得它的补码最高位变为0,此时该数据必然超出当前数据类型的范围,那么我们就可以得出结论:任意一个负整数,向左移动n位之后,仍然在数据类型给定范围内,那么它一定还是负整数。

证明:
要为负整数,那么最高位必然为1,所以当从左起第一个0出现时,0左边一定有至少一个1,而当这个0移动到最高位,前面的1必然超出数据类型范围。

结论:任意一个负整数A,向左移动n位之后得到B,B仍然在数据类型给定范围内,那么B一定还是负整数。

(emmmmm~~虽然篇幅有点长吧,但总算是解释清楚了(个人认为哈),如果有什么地方错误,还请纠正。感谢阅读!)