
大多数C编译器使用shift方法获取比调用乘法和除法子例程生成的代码更有效的代码.
最近,当我查看Java源代码时,我看到了一些位操作操作,并故意组织了shift操作. 我不知道是否不组织他们. 如果让我大吃一惊,那就让我们一起看吧!
移位操作是将值左右移动. 对于十进制系统,放大和缩小的效果是十倍,对于二进制系统,放大和缩小的效果是十倍.
我曾经忽略的一件事是C / C ++中的移位操作容易出错.
1. 哪些数据类型可以直接转换
Char,short,int,long,unsigned char,unsigned short,unsigned int,unsigned long可以移位,但double,float,bool和long double不能移位.
2. 签名数据类型的移位操作
对于带符号的数据类型,例如char,short,int和long:
负数的左移: 符号位始终为1,其他位向左移
正数的左移: 所有位向左移位,即<<,可能变为负数
将负数移至右侧: 取绝对值,然后移至右侧,然后取相反的数字
右移到正数: 所有位右移,即>>
在这里非常重要. 为什么会这样呢?我待会再解释!
3. 无符号数据类型的移位操作
对于无符号char,unsigned short,unsigned int,unsigned long,这些无符号数据类型:
没有特殊说明,请使用<<和>>运算符
逻辑移位
对于逻辑移位,不考虑符号位逻辑右移和算术右移,移位的结果只是所有数据位的移位. 根据移位操作的目的,向左移位时左位填充为0,向右移位时高位填充为0;
示例:
01010101 >> 3 = 00001010
01101011 << 3 = 01011000
Q1: 此时是否有问题,无论符号位如何,如果为负数,逻辑右移,结果将变为正数,例如

10000101 = -5 >> 1 = 01000010 = 66
是的,因为这是逻辑运算的特征,所以算术移位运算将仅考虑符号位!
Q2: 如果有符号数在逻辑上向左移动,导致符号位发生变化,这也是逻辑运算的特征吗?
例如: 10000001 = -1 << 1 = 00000010 = 2;
这只是溢出,不是功能. 算术移位中也存在类型溢出!
此外,C语言中没有无符号移位运算符,如何实现无符号右移?负数将无符号右移. 显然,符号位用0填充并变为正数. 这是没有意义的. 通常,正数用于无符号位移位. 您可以强制转换并使用除法!
措辞更严格:
1. 类型转换((unsigned int)x);
2. 用要移动的数字和0xFFFFFFFF执行与运算,并且可以将其转换为无符号类型(0xFFFFFFFF&x);
此处假定int类型为32位...
算术移位
算术是带有符号的数据,因此我们不能直接移动所有数字,这可能会使符号不正确.
一个非常重要的知识:
关于数字的移位,特别需要注意正数,这三个代码是相同的,因此左右移位都用0填充. 负数的补码需要注意,左移位在右边用0填充,需要向右移位在左边加1,有一个非常有趣的误解是符号位保持不变,只有数据位被移动,这是错误的,因为数据位和符号位是二进制的,在整体迁移过程中,符号位也跟风. 但是,为了确保右移与原始符号号相同,因此,当向右移时,负数在左侧补1.
例如,8位机器编号(补码): 1,110 0110,右移为: 1,111 0011,起始符号位变为当前数据位.
(1). 原始代码原始代码是一个附加的符号位,因此符号位保持不变,其余值位可作为逻辑移位处理.
为什么在这里说逻辑移位运算?
由于计算机中存储的数据以二进制补码存储,因此整数补码,反码和原始代码相同,因此对原始代码的算术移位运算是对正数的算术运算,因此使用逻辑转向处理!
(2). 逆码与原始码相似,符号位保持不变,其余数字位视为逻辑移位,但对于负数,当添加0时应添加1. 这是因为负数的反码是“ 0”,其效果与正数“ 1”相同.
(3). 观察补码表. 不难知道,当向左移出的数据数为“ 0”而负数为“ 1”(只有该值小于或等于最大值/ 2)时,一定不会发生溢出. 因为补码是计算机存储的一种形式,所以在实现硬件时很方便且很简单,
左移: 假定没有溢出发生,则数据的最高有效位直接移入符号位,最低位填充0(为什么负数的最低有效位不补1) ?如果补码用1补码,是正确的,但是补码用补码1补码,因此最低位仍用0填充.
右移: 假定没有溢出发生,符号位保持不变,并且符号位用于补全最大值.

例如,如果一个带符号的数字向左移动,C ++编译器将如何表现?
#include
#include
使用命名空间标准;
int main()
{
char a = -65;
a = a >> 1;
printf(“%d”,a);
返回0;
}
检查主要功能的汇编代码:
主要:
push rbp
mov rbp,rsp
sub rsp,0x10
mov BYTE PTR [rbp-0x1],0xbf
movsx eax,BYTE PTR [rbp-0x1]
sar eax,1
mov BYTE PTR [rbp-0x1],al
movsx eax,BYTE PTR [rbp-0x1]
mov esi,eax

mov edi,0x4007d5
mov eax,0x0
致电4005a0
mov eax,0x0
离开
ret
这里是对汇编知识的一些补充. 例如,MOV BYTE PTR [BX] 10H源操作数使用立即寻址,等效于直接分配10H,而目标操作数使用寄存器间接寻址逻辑右移和算术右移,假定BX内容(BX)= 1000H,(DS)= 2000H,则对应的物理地址为DS X 10H + BX = 21000H,现在对应于该物理地址的单元内容为10H
mov BYTE PTR [rbp-0x1],0xbf,这句话是汇编的,表示立即数0xbf被放入(DS)X 10H + rbp-0x1的物理地址中;对应于C ++中的char a = -65;
现在让我们看一下0xbf,我们知道-65 = 11000001,转换为补码,除了符号位,另一个取反并加一个,补码为10111111 = 0xbf !!!
也就是说,C ++在存储数字时使用补码形式;
10111111 >> 1,结果为11011111,将其转换为原始代码. 除符号位外,另一个取反并加一个,即10100001 = -33;
编译器的结果确实是-33
程序返回: 0
程序标准输出
-33
如果是-65 >> 2,结果将是什么?
11101111-> 10010001 = -17
为什么这里是-17!
10111111 >> 2,它变为101111,符号位仍为1,然后第七位为1或0!
上面描述了二进制补码,右移: 假设没有溢出发生,符号位保持不变,并且符号位用于补全最高位.
即第七位加1,即移位后变为11101111,原始码位为10010001 = -17;
如果-65 >> 3,则在移位10111之后,符号位为1,第七位加1,第六位!实际上,算术向右移动,左侧用符号位填充,即,正数用0填充,负数用1填充.

-65 >> 3 = 11110111,原始代码是10001001 = -9
了解编译器以补码形式执行算术移位运算时,请考虑算术右移的情况,然后考虑算术左移的情况,
左移的一种特殊情况是,当左移的数量超过数字类型的最大位数时,编译器将使用左移的位数对最大位数进行模运算,然后再将余数进行移位一点,不要先考虑这种情况!
考虑到正常情况,左移规则如下!
左移: 假设没有溢出发生,则最高有效数据直接移入符号位,最低位填充0
在左移描述中有一个假想的词,就是说如果没有溢出,如何确保没有溢出?
计算机执行此操作. 为了确保补码算术在向左移位时不会溢出,移位数据的最高有效位必须与符号位相同. 移位后的数据的最高有效位是什么,即移位后变为最高符号位的数据位. 在不发生溢出的前提下,当硬件对算术左移进行补充时,最有效地将数据移入符号位,而无需更改机器编号的符号.
移位数据的最高有效位必须与符号位相同吗?这句话是什么意思?这是否意味着算术左移与逻辑左移具有相同的含义? 〜...〜但是〜但是有什么意义〜?
以一个字节为例,可以表示的数字范围为-128〜127
首先查看正数,例如9,即00001001. 数据的最高有效位(符号位)和最高有效位为0. 向左移动以得到18的0001 0010,然后查看在负数-9(即-(0000 1001))处,即1111 0110 +1为1111 0111,数据的最高位(符号位)和最高位为1. 左移后为1110 1110,其中依次是(1110 1101 +1)-> 0001 0010,即-18.
如果数据的最高位和符号位不同,则表示该数字的绝对值超过64,因此在向左移动一位后不可避免地会溢出.
因此,[有效]最左移和最高数据位必须保持一致. 因此,算术左移与逻辑左移相同.
算术移位和逻辑移位的比较
根据上面的描述,可以总结一下(均为补码运算): 当[有效]左移最高位与数据最高位一致时,算术左移与逻辑左移相同,两者均为右补0,不一致时,算术左移溢出,毫无意义!逻辑上的右移非常简单,只要将二进制数向右移,并且左侧用0填充,算术向右移,并且左侧用符号位填充,即,正数用0填充,负数用1填充.
如果不清楚,可以回头看看上面的分析!
逻辑上的转换是最简单的. 无论向左或向右移动,空位都可以填充为0.
需要特别注意算术移位,原始符号位也应移位. 由于位移是宏观变化,因此不允许任何元素保持静止. 因此,当向左移动时,正数可能变为负数,负数可能变为正数. 由于丢失了向左移动的原始符号位,因此右侧填充了0. 右移不会改变符号,因为右移是将数据减半,并且减半不能减少到相反的符号. 左移可能会溢出,并且溢出的特征是符号过渡.
java移位运算符: <<(左移),>>(带符号右移)和>>>(无符号右移)
<<(左移)
向左移动的规则只记住一件事: 丢弃最高位,0填充最低位(这时也会溢出,但我们也应尽量避免这种溢出)
如果所移动的位数超过该类型的最大位数,则编译器将对所移动的位数进行模运算. 如果将int类型的33位移动了,则实际上只有332 = 1位被移动了. >>(向右移动带有符号)
右移规则只记住一件事: 符号位未更改,左侧填充符号位
将所有数字以二进制形式向右移动相应的位数,移出低位(放弃),并在高位填充空位的符号位,即为正数加零. 1代表负数>>>(无符号右移)<
无符号右移的规则只记住一件事: 忽略符号位扩展,0补充最高位
无符号右移规则与右移操作相同,除了填充时,无论左侧数字是否为负,都将填充0. 无符号右移操作仅针对负数计算,因为这对于正数是正确的. 该操作没有任何意义. 无符号右移运算符>>>仅适用于32位和64位值
本文来自电脑杂谈,转载请注明本文网址:
https://www.pc-fly.com/a/ruanjian/article-237924-1.html
减少人员及财产损失
e租宝利息这么高吗