大家好,这是我发布个人博客的第一条文章,本人缺乏经验,有任何问题欢迎大家指出,请大家多多担待。
位运算符
位运算符是一类直接对整数在内存中的二进制位进行操作的运算符。这些操作包括按位(AND)、按位或(OR)、按位异或(XOR)、按位取反(NOT)、左移(Shift Left)和右移(ShiftRight)等。位运算符允许程序员以非常底层和直接的方式处理数据,这在某些情况下可以提供比传统算术和逻辑运算符更高的效率和更灵活的数据操作能力。
下面对以上各运算符进行介绍
- 按位与(&):
- 对两个数的二进制表示进行逐位比较,只有两个相应的位都为1时,结果位才为1,否则为0。
- 常用于清除特定位(将特定位设置为0)或检查两个数的特定位是否同时为1。
在力扣LCR 070. 有序数组中的单一元素中有用到按位与方法其中在折半查找中使用
mid -= mid & 1;我们通过 mid -= mid & 1; 来调整 mid 的值,确保它是偶数。这是因为 mid & 1 的结果是mid 的最低位(二进制表示中的最后一位),如果它是1(即 mid & 1 是奇数),则通过减去1将其变为偶数。具体代码可以在力扣的官方题解中找到,大家可以尝试一下。
- 按位或(|):
- 对两个数的二进制表示进行逐位比较,只要两个相应的位中有一个为1,结果位就为1;只有当两个位都为0时,结果位才为0。
- 常用于设置特定位(将特定位设置为1)或合并两个数的二进制表示。
public class BitwiseOrExample { public static void main(String[] args) { int a = 5; // 二进制表示为 0000 0101 int b = 3; // 二进制表示为 0000 0011 int result = a | b; // 对a和b进行按位或操作 System.out.println("a = " + Integer.toBinaryString(a)); System.out.println("b = " + Integer.toBinaryString(b)); System.out.println("a | b = " + Integer.toBinaryString(result)); // 输出结果将是(其中为了简便我将前面的0全都省略): // a = 101 // b = 11 // a | b = 111,因为0000 0101 | 0000 0011 = 0000 0111 } }
在这个例子中,我们有两个整数
a
和b
,它们的二进制表示分别是0000 0101
和0000 0011
。我们使用按位或(|
)运算符对它们进行按位或操作,并将结果存储在变量result
中。
第一个位(最右边的位,即2的0次方位)上,a
和b
都是1,所以结果位也是1。
第二个位上,a
是0,b
是1,所以结果位是1。
第三个位上,a
是1,b
是0,所以结果位也是1(因为只要有一个1,结果位就是1)。
剩下的高位都是0,与任何数进行按位或操作都不会改变。
- 按位异或(^):
- 对两个数的二进制表示进行逐位比较,当两个相应的位不同时,结果位为1;相同时,结果位为0。
- 常用于切换特定位(将特定位取反)或检测两个数是否在某个或某些位上不同。
public class BitwiseXorExample { public static void main(String[] args) { int a = 6; // 二进制表示为 0000 0110 int b = 3; // 二进制表示为 0000 0011 int result = a ^ b; // 对a和b进行按位异或操作 System.out.println("a = " + Integer.toBinaryString(a)); System.out.println("b = " + Integer.toBinaryString(b)); System.out.println("a ^ b = " + Integer.toBinaryString(result)); // 输出结果将是(注意:Java的Integer.toBinaryString()方法可能不包括前导零): // a = 110 // b = 11 // a ^ b = 101,因为0000 0110 ^ 0000 0011 = 0000 0101 // 但实际打印时,为了清晰起见,我们可以添加缺失的前导零(这里仅为了说明): // a = 00000110 // b = 00000011 // a ^ b = 00000101 } }
在这个例子中,我们有两个整数
a
和b
,它们的二进制表示分别是0000 0110
和0000 0011
。我们使用按位异或(^
)运算符对它们进行按位异或操作,并将结果存储在变量result
中。
第一个位(最右边的位)上,a
是0,b
是1,所以结果位是。
第二个位上,a
是1,b
是1,所以结果位是0(因为两个位相同)。
第三个位上,a
是1,b
是0,所以结果位是1(因为两个位不同)。
剩下的高位都是0,与任何数进行按位异或操作都不会改变(因为任何数与0异或都保持原样)
- 按位取反(~):
- 对一个数的二进制表示进行逐位取反操作,即0变为1,1变为0。
- 需要注意的是,按位取反操作通常会影响数的符号位,因此在使用时需要特别小心。
public class BitwiseNotExample { public static void main(String[] args) { int a = 5; // 二进制表示为 0000 0101(在32位整数中,前面还有30个0未显示) int result = ~a; // 对a进行按位取反操作 // 直接打印结果可能不太直观,因为它是一个负数 // 我们可以通过将结果转换为无符号整数(在Java中并不直接支持,但可以通过其他方式模拟)来查看其二进制表示 // 或者,我们可以简单地查看结果的十进制值和其二进制补码表示 // 打印十进制值 System.out.println("a = " + a); System.out.println("~a = " + result); // 这将是一个负数 // 打印二进制补码表示(注意:这是模拟的,因为Java没有内置的无符号整数类型) System.out.println("a (binary) = " + Integer.toBinaryString(a)); // 对于负数,我们可以通过转换为正数再取反来查看其二进制表示(但注意,这不是按位取反的直接结果) // 更好的方法是直接解释二进制补码 System.out.println("~a (binary as if unsigned) = " + Integer.toBinaryString(~result & 0xFFFFFFFF)); // 使用掩码来模拟无符号整数 // 解释:~a 的二进制补码表示是一个负数,但我们可以通过将其与 0xFFFFFFFF 进行与操作来“模拟”无符号整数的二进制表示 // 实际上,~a 的二进制补码表示中的每一位都是 a 二进制表示中对应位的取反,包括前导的符号位(从1变为0,或从0变为1,取决于a的符号位) // 更直接地解释 ~a 的结果:它是一个整数,其二进制补码表示是 a 的二进制表示(包括所有前导0)的每一位取反 // 在这个例子中,~5 的结果是 -6,因为 5 的二进制补码是 0000 0000 0000 0000 0000 0000 0000 0101 // 按位取反后得到 1111 1111 1111 1111 1111 1111 1111 1010,这是 -6 的二进制补码表示(因为最高位是1,表示这是一个负数) } }
但是,请注意上面的代码片段中关于二进制表示的打印部分有一些误导性,因为Java中的
Integer.toBinaryString()
方法是为有符号整数设计的,并且直接打印负数的二进制补码表示可能不会很有用(除非你已经熟悉了二进制补码和如何解释它)。实际上,当你看到
~a
的结果是一个负数时,你应该知道这是因为在二进制补码表示中,按位取反操作将数的所有位取反,包括符号位,从而改变了数的符号并可能导致溢出(对于较小的正数,按位取反后通常会得到一个较大的负数,因为符号位从0变为1)。为了更直观地理解
~a
的结果,你可以将其视为一个与原始数a
在二进制表示上完全相反的数(但注意符号位的影响和二进制补码表示的规则)。在这个例子中,~5
的结果是-6
,因为5
的二进制补码取反后得到了-6
的二进制补码表示。
- 左移(<<):
- 将一个数的二进制表示向左移动指定的位数,移动过程中超出的部分将被丢弃,而在右侧新增的位将用0填充。
- 左移操作相当于乘以2的幂次方(在不考虑溢出和符号扩展的情况下)。
- 左移操作:使用
<<
运算符。 - 操作过程:将数的二进制表示向左移动指定的位数,右侧超出的位被丢弃,左侧新增的位用0填充。
- 结果:左移n位相当于乘以2的n次方(在不考虑整数溢出的情况下)。
举例:假设我们有一个整数a
,其值为6,我们想要将其向左移动2位。public class LeftShiftExample { public static void main(String[] args) { int a = 6; // 二进制表示为 0000 0110 int result = a << 2; // 将a向左移动2位 System.out.println("原始值: " + a); System.out.println("左移2位后的值: " + result); // 输出结果将是28 } }
- 右移(>>):
- 将一个数的二进制表示向右移动指定的位数。对于有符号整数,移动时左侧新增的位的行为取决于具体的编程语言和编译器(可能是用0填充,也可能是用符号位填充)。
- 右移操作相当于除以2的幂次方并向下取整(在不考虑负数时的算术右移)。
public class RightShiftExample { public static void main(String[] args) { int a = -8; // 二进制补码表示为(以32位为例)1111 1111 1111 1111 1111 1111 1111 1000 int result = a >> 3; // 将a向右移动3位 // 注意:直接打印result可能不会给你直观的二进制表示,但我们可以打印其十进制值 // 实际上,要准确知道result的二进制补码表示,你可能需要手动计算或使用额外的库来帮助你 System.out.println("原始值: " + a); System.out.println("右移3位后的值: " + result); // 输出结果将是一个负数,具体值取决于a的二进制补码右移3位后的结果 } }
本篇文章来自于个人总结和网络查询,有任何问题大家可以在评论区提出来,我会一一回复加以改正,谢谢大家的担待,再次感谢。