首页 > 编程语言 >Java位运算符代码演示

Java位运算符代码演示

时间:2024-06-07 23:33:37浏览次数:17  
标签:11111111 演示 int 补码 反码 运算符 intToBinaryString Java 原码

文章目录

原码、反码、补码

在计算机内,有符号数有3种表示方法:原码、反码和补码,机器数的最高位为符号位,符号位为0表示正数,符号位为1表示负数。

原码=符号位+真值的绝对值。如:3的原码(这里假设机器数的字长为8)是00000011,-3的原码是10000011。

对于正数,反码=原码;对于负数,反码=原码保留符号位+绝对值部分按位取反。如:-3的原码是10000011,反码是11111100。

对于正数,补码=原码;对于负数,补码=反码保留符号位+(绝对值部分+1)。如:-3的原码是10000011,补码是11111101。

总结:对于正数,原码=反码=补码;对于负数,反码=原码保留符号位+绝对值部分按位取反,补码=反码保留符号位+(绝对值部分+1),可见负数的原码反码补码的符号位都是1;对于0,+0和-0有两种不同的原码表示形式,一般统一将-0变为+0,即将0归入正数,所以0的原码=反码=补码。

问:原码的概念很好理解,为什么还要加入反码和补码的概念?
答:因为计算机中底层只能计算加法,所以需要将减法转换成加法来处理。如:3-3=3+(-3),原码不能解决这个问题,补码可以,反码可以看作是为了引出补码的概念。补码可以带符号位参与运算,计算机的数值型数据都是补码的形式存储的。如:3-3=3+(-3),3和-3用补码表示(00000011和11111101)并相加得到的结果是00000000,即0;3-5=3+(-5),-5的原码是10000101,反码是11111010,补码是11111011,3和-5用补码表示(00000011和11111011)并相加得到的结果是11111110,这是补码,符号位为1说明是个负数,将这个补码转换成原码(先得到反码:符号位不变,绝对值部分-1,得11111101,再得到原码:符号位不变,绝对值部分按位取反,得10000010,即-2),根据原码得到结果-2。

代码演示(Java)

Java的位运算符有:&(按位与)、|(按位或)、^(按位异或)、~(按位取反)、<<(左移)、>>(右移)、>>>(无符号右移,注意并没有无符号左移的位运算符)

  • 计算机的位只有1、0两个值,计算数值型数据的时候,使用位运算符比使用算术运算符要快。
    • 注:位操作符的结果都是int类型。
  • a&b:对比a、b的每个位,只有两者均为1时,结果为1,其他时候结果为0。可理解为,比较每个位,每个位取小那个值。
  • a|b:对比a、b的每个位,只要两者有一个为1,结果就为1,只有两者全为0时结果为0。可理解为,比较每个位,每个位取大那个值。
  • a^b:对比a、b的每个位,两者不同时结果为1,相同时结果为0。异或有翻转的作用。
  • ~a:对a的每个位进行取反操作。
  • a<<n:左移n位,溢出舍去,少了补n个0。在一些情况下,结果为 a ∗ 2 n a*2^n a∗2n。
    • 注:移位要先求模(包括左移右移无符号右移),再移。a移n位,实际上移动的位数是n%(32或64,a为int型就是32,long型就是64)。
  • a>>n:右移n位,溢出舍去,用符号位填充高位。结果为 a / 2 n a/2^n a/2n。
    • 注:结果向下取整,10/4=2.5,向下取整等于2;-10/4=-2.5,向下取整等于-3。
  • a>>>n:右移n位,溢出舍去,用0填充高位。在一些情况下,结果为 a / 2 n a/2^n a/2n。
public class TestBitOperations {

	public static void main(String[] args) {
		int a = 0b10; // 2,正数,原码=反码=补码=00000000 00000000 00000000 00000010
		int b = 010; // 8,正数,原码=反码=补码=00000000 00000000 00000000 00001000
		int c = 10; // 10,正数,原码=反码=补码=00000000 00000000 00000000 00001010
		int d = 0x10; // 16,正数,原码=反码=补码=00000000 00000000 00000000 00010000
		int negativeA = -a;// 负数,原码=10000000 00000000 00000000 00000010,反码=11111111 11111111 11111111 11111101,补码=11111111 11111111 11111111 11111110
		int negativeB = -b;// 负数,原码=10000000 00000000 00000000 00001000,反码=11111111 11111111 11111111 11110111,补码=11111111 11111111 11111111 11111000
		int negativeC = -c;// 负数,原码=10000000 00000000 00000000 00001010,反码=11111111 11111111 11111111 11110101,补码=11111111 11111111 11111111 11110110
		int negativeD = -d;// 负数,原码=10000000 00000000 00000000 00010000,反码=11111111 11111111 11111111 11101111,补码=11111111 11111111 11111111 11110000

		int[] numbers = { a, b, c, d, negativeA, negativeB, negativeC, negativeD};
		for (int element : numbers) {
			// Integer.toBinaryString(int i)只有在i为负数时,会返回补码
			System.out.printf("%d 在计算机中存储的是 %s%n", element, formatBinaryString(intToBinaryString(element)));
		}
		System.out.println("--------------------------------------------------------------------------------");

		showBitOperations(b, c, 2);
		System.out.println("--------------------------------------------------------------------------------");
		
		showBitOperations(c, d, 2);
		System.out.println("--------------------------------------------------------------------------------");
		
		showBitOperations(negativeB, negativeC, 2);
		System.out.println("--------------------------------------------------------------------------------");
		
		// 移位要先根据移的位数求模(int类型的数据模是32,long类型的数据模是64),再移。
		System.out.printf("%d(%s) 左移(<<) %d位得到%d(%s)%n", a, formatBinaryString(intToBinaryString(a)), 34, c, formatBinaryString(intToBinaryString(c)));
		System.out.println("--------------------------------------------------------------------------------");
		
		// 判断奇偶
		for (int i = 0; i < 10; i++) {
			String str = (i & 1) == 1 ? "是奇数" : "是偶数";
			System.out.println(i + str);
		}
		System.out.println("--------------------------------------------------------------------------------");
	}

	/**
	 * 将int值转换为二进制字符串表示。
	 * 
	 * @param n 要转换的int值
	 * @return 对应的二进制字符串
	 */
	private static String intToBinaryString(int n) {
		StringBuilder binary = new StringBuilder(32);
		for (int i = 31; i >= 0; i--) {
			// 通过位与运算和位移操作获取每一位的值,并追加到字符串中
			binary.append((n >> i & 1));
		}
		return binary.toString();
	}
	
	/**
	 * 每8位加个空格,方便观察
	 * @param binaryString 输入的二进制字符串
	 * @return 格式化后的字符串
	 */
	private static String formatBinaryString(String binaryString) {
		if (binaryString == null) {
            return null; // 或者抛出异常,根据实际需求处理null输入
        }
        
        StringBuilder sb = new StringBuilder();
        int length = binaryString.length();
        for (int i = 0; i < length; i++) {
            sb.append(binaryString.charAt(i)); // 添加当前字符
            if ((i + 1) % 8 == 0) { // 检查是否达到每隔8字符的条件
                sb.append(" "); // 添加空格
            }
        }
        return sb.toString(); // 返回格式化后的字符串
	}
	
	/**
	 * 演示Java的位运算符(&、|、^、~、<<、>>、>>>)
	 * @param a
	 * @param b
	 * @param n 位移几位
	 */
	private static void showBitOperations(int a, int b, int n) {
		// 按位与
		int aAndB = a & b;
		System.out.printf("%d(%s) 按位与(&) %d(%s)得到%d(%s)%n", a, formatBinaryString(intToBinaryString(a)), b, formatBinaryString(intToBinaryString(b)), aAndB, formatBinaryString(intToBinaryString(aAndB)));

		// 按位或
		int aOrB = a | b;
		System.out.printf("%d(%s) 按位或(|) %d(%s)得到%d(%s)%n", a, formatBinaryString(intToBinaryString(a)), b, formatBinaryString(intToBinaryString(b)), aOrB, formatBinaryString(intToBinaryString(aOrB)));

		// 按位异或(不同为1,同为0)
		int aXorB = a ^ b;
		System.out.printf("%d(%s) 按位或(^) %d(%s)得到%d(%s)%n", a, formatBinaryString(intToBinaryString(a)), b, formatBinaryString(intToBinaryString(b)), aXorB, formatBinaryString(intToBinaryString(aXorB)));

		// 按位取反
		int notA = ~a;
		int notB = ~b;
		System.out.printf("%d(%s) 按位取反(~)后得到 %d(%s)%n", a, formatBinaryString(intToBinaryString(a)), notA, formatBinaryString(intToBinaryString(notA)));
		System.out.printf("%d(%s) 按位取反(~)后得到 %d(%s)%n", b, formatBinaryString(intToBinaryString(b)), notB, formatBinaryString(intToBinaryString(notB)));

		// 左移
		int leftShiftA = a << n;
		int leftShiftB = b << n;
		System.out.printf("%d(%s) 左移(<<) %d位得到%d(%s)%n", a, formatBinaryString(intToBinaryString(a)), n, leftShiftA, formatBinaryString(intToBinaryString(leftShiftA)));
		System.out.printf("%d(%s) 左移(<<) %d位得到%d(%s)%n", b, formatBinaryString(intToBinaryString(b)), n, leftShiftB, formatBinaryString(intToBinaryString(leftShiftB)));
		
		// 右移
		int rightShiftA = a >> n;
		int rightShiftB = b >> n;
		System.out.printf("%d(%s) 右移(>>) %d位得到%d(%s)%n", a, formatBinaryString(intToBinaryString(a)), n, rightShiftA, formatBinaryString(intToBinaryString(rightShiftA)));
		System.out.printf("%d(%s) 右移(>>) %d位得到%d(%s)%n", b, formatBinaryString(intToBinaryString(b)), n, rightShiftB, formatBinaryString(intToBinaryString(rightShiftB)));

		// 无符号右移
		int unsignedRightShiftA = a >>> n;
		int unsignedRightShiftB = b >>> n;
		System.out.printf("%d(%s) 无符号右移(>>>) %d位得到%d(%s)%n", a, formatBinaryString(intToBinaryString(a)), n, unsignedRightShiftA, formatBinaryString(intToBinaryString(unsignedRightShiftA)));
		System.out.printf("%d(%s) 无符号右移(>>>) %d位得到%d(%s)%n", b, formatBinaryString(intToBinaryString(b)), n, unsignedRightShiftB, formatBinaryString(intToBinaryString(unsignedRightShiftB)));
	}
}

运行结果:在这里插入图片描述

应用

  1. 判断某数的奇偶:(n & 1) == 1(true为奇、false为偶)

  2. 判断两个数是否异号:(a^b)<0(true为异号、false为同号)

  3. 速算某数*2的n次方:某数<<n

    1. 注:移位要先求模(包括左移右移无符号右移),再移。某数移n位,实际上移动的位数是n%(32或64,某数为int型就是32,long型就是64)。
    2. 注:左移可能导致符号位改变,此时结果就不对了。
    3. 注:左移可能导致结果超过某数数据类型的上限(int型是正负21亿多),此时结果就不对了。
  4. 速算某数/2的n次方:某数>>n

    1. 注:移位要先求模(包括左移右移无符号右移),再移。某数移n位,实际上移动的位数是n%(32或64,某数为int型就是32,long型就是64)。
    2. 注:结果向下取整,10/4=2.5,向下取整等于2;-10/4=-2.5,向下取整等于-3。
  5. 某数+1:-~某数

    1. 证明:因为n和~n的每个位相加都是1(包括符号位),所以n+(~n)=-1,所以n+1=-~n。
  6. 某数-1:~-某数

    1. 证明:(TODO)
  7. 不用临时变量交换两个数:

int a = 8;
int b = 10;
a = a^b;
b = b^a;
a = a^b;
1. 证明:需要用到^的性质。
① a ^ a =0 (任何数异或本身结果为0)
② a ^ b =b ^ a (交换律)
③ a ^ b ^ c = a ^ (b ^ c) = (a ^ b) ^ c (结合律)
④ 0 ^ a = a (异或0具有保持的特点)
⑤ a ^ b ^ a = b (根据①②④可得)
a=a原^b原,b=b原^a=b原^(a原^b原)=a原,至此,a存放的数据为a原^b原,b存放的数据为a原,只要再执行a^a原就能实现a里面存放b原的数据了,但a原的数据已经不在a里面了,而是在b里面了,所以a=a^b=(a原^b原)^a原=b原。
  1. 求某数的绝对值: |n| = (n^(n>>31))-(n>>31)(这里的n是32位的数据)
    1. 证明:(TODO)
  2. 字母转小写:字母|" "
    1. 证明:空格的ASCII码是32(即00100000),字母的ASCII码的范围是A~Z(65~90,即010000001~01011010)、a~z(97~122,即01100001~01111010),每个小写字母比对应的大写字母的ASCII码大32(即00100000),刚好为空格的ASCII码。字母|" "表示比较字母和空格的每一个位,取大那个值,这样可起到将字母的第三个位恒定设为1,字母的其他位数据不变,结果就相当于将字母转小写(如果字母原来是小写,就不变)。
  3. 字母转大写:字母&"_"
    1. 证明:下划线的ASCII码是95(即01011111),字母&"_"表示比较字母和_的每一个位,取最小那个值,这样可起到将字母的第三个位恒定设为0,字母的其他位数据不变,结果就相当于将字母转大写(如果字母原来是大写,就不变)。
  4. 大小写互换:字母^" "
    1. 证明:字母^" "表示字母的第三个位10互换(1^1=0,0^1=1),字母的其他位数据不变,结果就相当于将字母切换大小写。

参考

  1. 《Java核心技术 卷1 基础知识 原书第11版》 机械工业出版社 Cay S.Horstmann著 林琪、苏钰涵等译
  2. Java中的位运算 by 柯柯不会Java
  3. 一篇搞定位运算——java位运算详解 by 青癯

标签:11111111,演示,int,补码,反码,运算符,intToBinaryString,Java,原码
From: https://blog.csdn.net/warcarlyp/article/details/139537089

相关文章

  • 重学java 58.红黑树相关集合
    现在还来得及                ——24.6.3一、TreeSet1.概述:        Treeset是set的实现类2.特点:        a.对元素进行排序        b.无索引        c.不能存null        d.线程不安全    ......
  • Java中运算与规则相关细节
    每日一句对待已然的事实,最快速度通关方法就是:觉察自己的心理和情绪,看见和观照,不断深呼吸,直到放下抗拒放弃排斥,直到接受事实全然接纳,全然巨服。对事实说:“是的”。然后看看,在接纳的基础上我还能做些什么。学习内容模运算a%b=a-a/b*b;符号取决于a++–自增和自......
  • Java 八股文-基础篇
    Java基础一、Java概述1.什么是Java?Java是一门面向对象的编程语言,不仅吸收了C++语言的各种优点,还摒弃了C++里难以理解的多继承、指针等概念,因此Java语言具有功能强大和简单易用两个特征。Java语言作为静态面向对象编程语言的优秀代表,极好地实现了面向对象理论,允许程......
  • Java为什么会成为现在主流的编程语言
    JDK动态代理 是Java标准库提供的一种实现动态代理的方式。具体来说,它允许在运行时动态地创建对象,并将方法调用重定向到代理对象中的处理程序(InvocationHandler)。其核心原理是通过反射机制,在运行时生成一个实现了被代理对象接口的匿名类,并通过该匿名类来创建代理对象。这个匿名......
  • Java 的动态代理是基于什么原理?
    Java的动态代理是基于以下原理实现的:代理模式:代理模式是一种设计模式,通过创建一个代理对象来控制对真实对象的访问。代理对象可以在调用真实对象之前或之后增加额外的逻辑,比如权限验证、记录日志等。代理模式的主要目的是在不修改原有代码的基础上,扩展对象的行为。动......
  • Java基础——数组应用之StringBuilder类和StringBuffer类
    系列文章目录文章目录系列文章目录前言一、StringBuffer类二、StringBuffer概述三、StringBuffer方法四、StringBuilder类五、String、StringBuffer、StringBuilder的区别前言前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点......
  • JavaScript中的async/await
    async/await是什么?async是一个修饰符,async定义的函数会默认的返回一个Promise对象resolve的值,因此对async函数可以直接进行then操作,返回的值即为then方法的传入函数。await也是一个修饰符,await关键字只能放在async函数内部,await关键字的作用就是获取Promise中返回......
  • 【Java笔记】第十章:接口
    一、理解1.接口:是一种标准,接口的实现者和使用者都必须遵循的约定2.语法特点:(1)接口的关键字:interface(2)接口的语法:   interface接口名{}(3)接口不能创建对象,可以声明引用(4)接口中的属性都是公开的、静态的、常量(默认被public、static、final修饰)(......
  • 在Java、Java Web中放置图片、视频、音频、图像文件的方法
    在Java软件中放置图片,通常涉及将图片文件(如JPEG、PNG等)作为资源包含在我们的项目中,并在代码中通过适当的方式引用这些资源。这可以通过多种方式实现,但最常见的是在Java桌面应用(如Swing或JavaFX)或Web应用(如Servlet/JSP)中。1.如何在Java中如何放置图片以下是一个在JavaSwing桌......
  • java mqtt自动重连注意点
    1、在使用Java的 org.eclipse.paho.client.mqttv3 MQTT客户端库时,options.setAutomaticReconnect(false) 的设置是用来指定在连接丢失后,客户端是否应该自动尝试重新连接。将其设置为 false 意味着如果连接丢失,客户端不会自动尝试重新连接。然而,即使设置了自动重连为 fa......