位运算专题二:左移、右移、异或

1. 左移<<:将整型数转为二进制补码形式表达后在其末尾添0再截取需要的低位即可。

 分析如下代码(有符号数在左移时会在正负间徘徊):

#include<stdio.h>
int main() {
	printf("对于无符号数:\n");
	unsigned char a = 0xaa; //无符号整型数a=1010 1010
	for (int i = 0; i < 8; i++) {
		printf("%d\n", a);
		a = a << 1;
	}
	printf("对于有符号数:\n");
	char x = 0xaa; //有符号数 会在正负之间徘徊,由于首位为符号位
	for (int i = 0; i < 8; i++) {
		printf("%d\n", x);
		x = x << 1;
	}
	return 0;
}

运行结果如图:

分析:对于无符号unsigned char a(有符号char x)=1010 1010(初始a=170,x=-86),其左移一位<<1变化过程为:1 0101 0100(a=84,x=84),10 1010 1000(a=168,x=-88),101 0101 0000(a=80,x=80),1010 1010 0000(a=160,x=-96),1 0101 0100 0000(a=64,x=64),10 1010 1000 0000(a=128,x=-128),101 0101 0000 0000(a=x=0)。由于有符号数首位代表符号位(除了char a=128为a=1000 0000=-128,首位既是符号位又是数值位),因此会在正负之间徘徊

附加:对于如下代码,当int a=1=0000 0000 0000 0000 0000 0000 0000 0001时,不断地左移1位在末尾补0就意味着不断地给该数*2直到到达整型数据的最小负数1000 0000 0000 0000 0000 0000 0000 0000(首位既是符号位又是数值位)。

2. 右移>>:对于无符号数:右移=在其二进制形式最左边添0;有符号数:在其二进制补码形式左端添符号位!!再截取需要的低位即可。

对于无符号数而言:右移一位相当于十进制形式/2;左移一位相当于十进制形式*2。

3. 典型例题之求二进制数中有几个1。相信大家都见过的做法就是:i=i&(i-1);而求二进制形式中有几个0则是i=i|(i+1)。如下面的代码:

#include<stdio.h>
int Get_0_num(int x) {
	int sum0 = 0;
	while (x+1 != 0) {
		//循环终止条件x+1!=0:通过或运算(1||任何数=1)将该数的每一位0都变成了1,全1状态下即为计算结束。
		x = x | (x + 1);
		sum0++;
	}
	return sum0;
}
int Get_1_num(int x) {
	int sum1 = 0;
	while (x != 0) {
		//通过与运算(0&&任何数=0)将该数的1逐渐变为0,最终该数为0,停止计算。
		x = x & (x - 1);
		sum1++;
	}
	return sum1;
}
int main() {
	int a = 0x12345678; //a=0001 0010 0011 0100 0101 0110 0111 1000
	int num1 = Get_1_num(a); //13个1 
	int num0 = Get_0_num(a); //19个0
	printf("num_1:%d\nnum_0:%d\n", num1,num0);
	return 0;
}

 第二种做法:通过x=x>>1一位一位判断最终得二进制中有几个1(只适用于有符号数二进制首位为0无符号数!)

#include<stdio.h>
int Get_1_num(int x) {
	int num = 0;
	while (x) {
		if (x & 0x1) {
			//通过x&0x1从低位开始一位一位判断:x&0x1=0则最低位=0,x>>1判断下一位。
			num += 1; //当x&0x1!=0:即该最低位为1,num++
		}
		x = x >> 1;//每判断一次右移一位,直到移到x=0判断完毕退出循环
	}
	return num;
}
int main() {
	int a = 0x12345678; //a=0001 0010 0011 0100 0101 0110 0111 1000
	int num = Get_1_num(a); //13个1 
	printf("num_1:%d\n", num);
	return 0;
}

 3. 异或^

(1)异或运算符运算法则:两个二进制数(补码形式)进行比较:相同为0,相异为1

#include<stdio.h>
int main() {
	char a = 0x89;//1000 1001
	char b = 0x76;//0111 0110
	char c = a ^ b;//1111 1111=-1(对于有符号数而言:全1=最大的负数-1)
	printf("%d\n", c);
	return 0;
}

由于为有符号数char类型,故异或结果:1111 1111=最大的负数-1(1111 1111=255还是-1取决于其为有符号数还是无符号数。一切数据在计算机中以补码形式存放!-1:原码1000 0001,补码:1111 1111)

(2)性质:a^(b^c)=(a^b)^c(可结合律),任何二进制数:a^a=0(相同为0,相异为1),a^0=aa^b^a=b

(3)利用异或运算符交换两个数(利用性质)

#include<stdio.h>
int main() {
	int a = 10,b = 20;
	a = a ^ b;
	b = a ^ b; //b=a^b=(a^b)^b=a^0=a
	a = a ^ b; //a=a^b=a^(a^b)=0^b=b
	printf("a=%d,b=%d\n", a, b);
	return 0;
}

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 以下是一个基于Verilog语言的8位流水灯程序,实现了双灯左移和闪烁效果: ```verilog module led_flasher( input clk, output reg [7:0] led ); reg [2:0] cnt; reg [1:0] direction; always @(posedge clk) begin if (cnt == 3'b111) begin cnt <= 0; if (direction == 2'b00) begin led <= {led[6:0], led[7]}; end else if (direction == 2'b01) begin led <= {led[0], led[7:1]}; end else begin led <= {led[0], led[7:1]}; end end else begin cnt <= cnt + 1; if (cnt == 3'b011) begin led <= 8'b11111111; end else if (cnt == 3'b111) begin led <= 8'b00000000; end end end always @(posedge clk) begin if (cnt == 3'b111) begin cnt <= 0; if (direction == 2'b00) begin direction <= 2'b01; end else if (direction == 2'b01) begin direction <= 2'b10; end else begin direction <= 2'b00; end end else begin cnt <= cnt + 1; end end endmodule ``` 该程序中使用了两个`always`块。第一个块在每个时钟上升沿时更新`led`的值,实现了双灯左移和闪烁。变量`cnt`用于计数,变量`direction`用于标记灯的动方向。 第二个块在每个时钟上升沿时更新`direction`的值,实现了灯的方向切换。当`cnt`等于7时,即8个时钟周期过去后,计数器归零,并根据`direction`的值更新`led`的值。 这样,当时钟信号输入时,LED灯就会按照预设的模式流动起来。 ### 回答2: 8位流水灯程序verilog_花样流水灯2:双灯左移与闪烁。 流水灯是一种常见的LED灯光效果,可以通过控制单个或多个LED的亮灭顺序,营造出不同的灯光效果。在Verilog语言中,我们可以通过定义一个计数器和一个位寄存器来实现流水灯的效果。 这里,我们要实现的是双灯左移与闪烁的花样流水灯。具体的实现步骤如下: 1. 定义一个8位计数器,用于控制LED灯亮灭的顺序。初始值为0000 0001,即最边的LED灯亮起。 2. 定义一个8位位寄存器,用于存储LED灯的状态。初始值为0000 0001,与计数器的值相同。 3. 根据计数器的值,确定两个LED灯的位置。当计数器的值为奇数时,两个LED灯依次左移;当计数器的值为偶数时,两个LED灯依次。 4. 每经过一定时间,改变计数器的值,即改变LED灯的位置。这里可以通过使用时钟触发器来实现定时效果。 5. 每隔一定时间,改变位寄存器的值,即改变LED灯的状态,实现闪烁效果。这里可以通过使用时钟触发器和一个定时器来实现闪烁效果。 通过以上步骤,我们可以实现双灯左移与闪烁的花样流水灯效果。根据需要,可以调整计数器和位寄存器的位数,来改变LED灯的数量和流水灯的效果。可以根据具体的硬件平台和开发板,进行相应的电路连接和代码调试,来实现该流水灯程序。 ### 回答3: 花样流水灯2是一种具有双灯左移和闪烁效果的流水灯程序。使用Verilog语言编写,可以实现这种效果。 程序使用一个8位的寄存器来存储灯的状态,每一位代表一个灯的亮灭状态。初始时,所有灯都是熄灭的。 程序的主要逻辑是通过运算运算来实现双灯左移和闪烁效果。具体实现如下: 1. 定义一个计数器变量count,用于控制灯的闪烁频率。初始时,count为0。 2. 在一个无限循环中,首先判断count是否达到了闪烁频率。如果达到了,则执行闪烁操作,即将所有灯的状态取反,然后将count重置为0。否则,继续累加count。 3. 接下来,判断count的奇偶性来确定灯的动方向。如果count为偶数,则执行操作,即将寄存器的值一位,并通过运算将最边的灯置为亮灭状态需要变换的值。如果count为奇数,则执行左移操作,即将寄存器的值左移一位,并通过运算将最左边的灯置为亮灭状态需要变换的值。 4. 将寄存器的值输出到LED灯组,实现对应的灯亮灭效果。 5. 结束循环,等待计数器继续累加。 通过上述步骤,可以实现双灯左移和闪烁效果的花样流水灯程序。可以根据需要调整闪烁频率和灯的动速度来实现不同的效果。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值