移位操作
算数移位与逻辑移位在进行嵌入式开发中移位操作算是大家用得比较频繁的一个操作符,因为大部分外设寄存器的每个位一般都代表着一项功能的使能或者选择,这样我们需要对一个或者多个置位或者清零等就会考虑使用移位操作,如下定义的宏大家应该不陌生。
2# define SET_BIT(x,n) (x|(1<<(n-1)))
3# define CLEAR_BIT(x,n) (x&~(1<<(n-1)))
那么大家在使用这两个宏的时候可能对于x变量的属性并没有太多的关注,因为大部分的外设寄存器都是用的无符号整形来表示的,如果是有符号整形呢?或者是浮点呢?最后的结果是这样的呢?1逻辑移位 逻辑移位运算简单的说就是不考虑符号位的移位处理,左移低位补0,右移高位补0,仅仅只是一种逻辑上的移动,所以对于无符号类型一般属于逻辑移位。
https://mmbiz.qpic.cn/mmbiz_png/DRH6rnpl6QfHEvOOR5ia11sZpPmNIFoyIv71ZZeicCVE7mGCicOdwdmJgbbtbeIqlevoF79bZ7zmeXjibGnaeGhib7w/640?wx_fmt=png
2算术移位 算数移位运算与逻辑移位的区别是其会考虑算数数值问题,所以会考虑符号位的处理,一般算数移位仅仅只针对有符号整形,其左移采用逻辑移位高位移出,低位补零,而右移则是高位用符号位填充,低位移除。
https://mmbiz.qpic.cn/mmbiz_png/DRH6rnpl6QfHEvOOR5ia11sZpPmNIFoyIQmkwWv0EADTWQNHlDhOGCccm2bTicIkgfuNcTs6hSaHXxwLzqdPyhMA/640?wx_fmt=png
看到这里一些小伙伴该疑惑了,为什么逻辑左移和算数左移是一样的呢 ? 难道不是算数左移也保留符号位吗?又或者说有符号数到底使用的是逻辑左移还是逻辑右移?下面我将为大家一一解答。
参考Demo:
1 #include <stdio.h>
2 #include <stdlib.h>
3
4 char sVal1 = 64;
5 char sVal2 = -7;
6
7 /********************************
8* Fuciton: 测量移位
9* Author : (公众号:最后一个bug)
10 *******************************/
11int main(int argc, char *argv[]) {
12
13 printf("sVal1 = %d\n",sVal1);
14 sVal1= sVal1<<1;
15 printf("sVal1<<1 = %d\n",sVal1);
16
17 printf("sVal2 = %d\n",sVal2);
18 sVal2= sVal2<<1;
19 printf("sVal2<<1 = %d\n",sVal2);
20
21 sVal2= sVal2<<4;
22 printf("sVal2<<4 = %d\n",sVal2);
23
25 return 0;
26}
运行结果如下图:
图片
分析一下:
从上面的结果可以看出,左移运算并不是与*2等价的,其存在溢出问题,由于符号位会被低位代替,所以其最终的符号由所移动的最后一位决定,大家在使用的时候需要注意。
5、浮点运算的移位问题
我们对浮点数一般都不进行移位操作,并且大部分编译都是禁止该类语法操作,因为浮点存储格式中具体的bit段是有具体含义的,并且控制在固定的bit上(可以参考:【典藏】别怪"浮点数"太坑(C语言版本)),比如你把阶码移动到了尾码,那么这个移位操作的结果代表什么意义?根本无法理解,不过有些小伙伴为了能够获得更高的效率,想直接通过移位来代替*2运算,会考虑直接操作阶码来进行处理,可以参考如下代码:
硬核代码:
1#include <stdio.h>
2#include <stdlib.h>
3
4float fVal = 3.123;
5double dVal = 3.123;
6
7//单精度浮点移位
8#defineFLOAT_SFT(f ,n ) ({ \
9 int itemp = *(int*)&f;\
10 itemp += n * 0x00800000u; \
11 *(float*)&itemp;})
12
13//双精度浮点移位
14#defineDOUBLE_SFT(d ,n ) ({ \
15 long long lltemp = *(long long*)&d;\
16 lltemp += n * 0x0010000000000000LL;\
17 *(double*)&lltemp;})
18
19/********************************
20 * Fuciton: 测量移位
21 * Author : (公众号:最后一个bug)
22 *******************************/
23int main(int argc, char *argv[]) {
24
25 printf("FLOAT_SFT(fVal ,1) = %f\n",FLOAT_SFT(fVal ,1));
26 printf("FLOAT_SFT(fVal ,-1)= %f\n",FLOAT_SFT(fVal ,-1));
27
28 printf("DOUBLE_SFT(dVal ,1)= %4.6f\n",DOUBLE_SFT(dVal ,1));
29 printf("DOUBLE_SFT(dVal ,-1) = %4.6f\n",DOUBLE_SFT(dVal ,-1));
30
32 return 0;
33}
简单分析一下:
上面的代码应该还是非常有意思的,可以真正体会到浮点数据的存储机构并且区分float类型和double类型的存储区别;
同时使用指针对数据进行转化也得到了非常好的应用,特别是大家在以后通信等字节传输过程中需要使用到拆字节传输,也能够在这个实例中受益。
这个是继承的寄存器的功能。汇编。 在单片机 C 语言编程里,移位操作是常用的位操作手段,它能对数据的二进制位进行左移或者右移操作。 不同编译器对负数的右移处理可能不同,建议优先使用无符号数。 移位的位数不超过数据类型的位数,否则结果可能不符合预期。 移位操作是一种常用的位操作技术,它允许程序员对数据的位进行左移或右移操作。 当左移操作的位数超过操作数的位数时,可能会导致数据溢出或未定义行为。因此,在进行左移操作时,应确保移位的位数不超过操作数的位数减1。 移位操作比乘除法快得多,尤其在需要快速计算的场合 在进行移位操作时,需要注意溢出问题。例如,对于8位无符号整数,左移超过7位会导致数据丢失。 左移可能丢失高位数据 移位操作的结果取决于操作数的数据类型(有符号或无符号)。对于无符号数,空出的位总是用0填充;对于有符号数,则可能用符号位填充。 在进行左移操作时,要注意数据溢出的问题。如果左移后的数据超出了数据类型所能表示的范围,会导致数据丢失。 在资源受限的单片机中,合理使用移位操作可显著提升代码效率和硬件控制能力,但需严格注意边界条件和硬件特性! 移位操作的结果类型取决于操作数的数据类型。对于无符号数,移位操作的结果也是无符号的;对于有符号数,结果类型保持为有符号。 在通信协议中,数据常常以二进制位的形式进行传输和处理。移位操作可以用于解析和封装通信数据,提取或设置特定的位信息。 左移操作可能会导致数据溢出,特别是当移位后的结果超过数据类型的范围时。 不要对负数进行左移操作,因为这将导致未定义的行为。 右移操作时,对于无符号整数是逻辑右移,对于有符号整数通常是算术右移。 左移可能导致溢出(未定义行为),右移是算术右移(补符号位)。 左移溢出后也不会保存进其他空间
页:
[1]
2