深入理解位运算以及位运算在Java源码中的运用

一、什么是位运算

程序中的数在计算机中都是以二进制形式存储,位运算就是直接对整数在内存中的二进制位进行操作,位运算 包括位逻辑运算和移位运算,位逻辑运算能够方便设置或屏蔽某个字节的一位或几位,也可以对两个数按位相加,移位运算可以对内存中某个二进制左移或右移几位

二、Java中提供的七种位运算

2.1、位运算核心操作

位运算符

 名称

 含义

 举例

 &

按位与

将参与运算的两个二进制数进行&与运算,如果两个二进制位都是1,则与运算的结果为1,其他全都为0。(0与任意数N&运算都是0)

a&b

 |

按位或

将参与运算的两个二进制数进行|或运算,两个二进制位只要其中1个是1 ,那么就是1,如果2个二进制位都是0则表示0。(0与任意数N|运算都是任意数N)

a|b 

按位异或

将参与运算的两个二进制数进行^异或运算,如果2个二进制位都是0或者都是1,那么就是0,如果两个二进制位不同,则为1。任何数与0异或运算都是其本身,任何数与自己进行异或运算结果都是0

 a^b

按位非

一元操作符,按位取反。每个二进制位上都取相反值,1变成0,0变成1。

 ~a

<< 

左移

将一个数各二进制位全部向左移动若干位,左移运算没有有符号和无符号左移动,在左移时,移除高位的同时在低位补0

a<<2 

>> 

右移

又称为有符号右移。将一个数各二进制位全部向右移动若干位,若参与运算的数字为正数,则在高位补0;若为负数,则在高位补1。

a>>2 

>>> 

无符号右移 

将一个数各二进制位全部向右移动若干位,右移之后左边都是补上0。忽略符号位 

 a>>>2

  位运算优先级从高到低依次为:取反(~)、左移位(<<)、右移位(>>)、无符号右一(>>>)、位与(&)、位异或(^)、位或(|)

2.2、位运算扩展操作

&= 按位与赋值

|= 按位或赋值

^=按位非赋值

<<= 赋值左移

>>=右移赋值

>>>=无符号右移赋值

四、位运算的应用

1、判断奇偶数

if(n&1==1){

//n是个奇数

}

其核心就是判断二进制最后一位是否为1,若是,则结果加上2^0=1,一定是奇数

2、交换两个数

a = a ^ b;

b = a^b  //b = (a^b)^b=a^0 = a

a = a^b // a = (a^b)^a = b

3、变化符号

通过对数值X取反并加1来改变其符号

x = ~x + 1;

4、清除最低位的1

使用 x & (x - 1) 可以将最低位的 1 清零,这个技巧可以用于计算二进制中 1 的数量或者确定一个数是否为2的幂

5、得到最低位的1

使用 x & (-x) 可以提取出 x 的最低位的 1。

6、设置和清除特定位

设置(Set)位:要设置 x 的第 n 位为 1,可以用 x |= (1 << n)。

清除(Clear)位:要清除 x 的第 n 位,可以用 x &= ~(1 << n)。

7、位运算在ByteBuffer源码中的应用

static int getIntB(ByteBuffer bb, int bi) {
        return makeInt(bb._get(bi    ),
                       bb._get(bi + 1),
                       bb._get(bi + 2),
                       bb._get(bi + 3));
    }

// -- get/put int --

    static private int makeInt(byte b3, byte b2, byte b1, byte b0) {
        
        return (((b3) << 24) |
                ((b2 & 0xff) << 16) |
                ((b1 & 0xff) <<  8) |
                ((b0 & 0xff)      ));
    }

//(b3) << 24)将 b3 左移 24 位。由于 int 类型是 32 位的,因此将 b3 左移 24 位可以将 b3 放到结果的最高位(最高有效字节位置)。

// b2 & 0xff:这一步是为了确保 b2 只取其最低的 8 位。因为 byte 类型是有符号的,如果 b2 的最高位是 1,则整数会被视为负数,这不是我们想要的。通过 & 0xff 操作,可以将 b2 转换为无符号整数,并且只保留其最低的 8 位。

// << 16:将 b2 左移 16 位,将其放置到结果的第二个字节的位置。

// b1 & 0xff:同样的操作,确保 b1 只取其最低的 8 位。
// << 8:将 b1 左移 8 位,将其放置到结果的第三个字节的位置。
// b0 & 0xff:同样的操作,确保 b0 只取其最低的 8 位。

// 最后一步不需要左移,因为 b0 将放置在结果的最低字节的位置。

这段代码实现了将四个字节按照大端字节序(Big Endian)组合成一个整数的功能。在这个函数中,b3 表示最高有效字节(Most Significant Byte,MSB),b0 表示最低有效字节(Least Significant Byte,LSB)

通过按照大端字节序的顺序组合字节,确保了生成的整数的高位包含了原始字节序列中的高位字节,低位包含了原始字节序列中的低位字节。

8、位运算实现加减乘除

1)加法

加法可以通过迭代使用异或(^)来计算无进位和,与(&)然后左移来计算进位,然后将它们加在一起,直到没有进位为止。

public class BitwiseOperations {

    public int add(int a, int b) {

        while (b != 0) {

            int sum = a ^ b; // 无进位和

            int carry = (a & b) << 1; // 进位

            a = sum;

            b = carry;

        }

        return a;

    }

}

2)减法

减法可以通过取补码来实现。要从a中减去b,我们可以添加b的补码。补码可以通过取反(~)再加1得到

add函数在二进制加法中已经实现

public int subtract(int a, int b) {

    // 加上b的补码(即 -b)

    return add(a, add(~b, 1));

}

3)乘法

乘法可以通过位移和加法来实现。我们对乘数进行迭代,如果当前位为1,则将被乘数加到结果上,然后被乘数左移一位。

public int multiply(int a, int b) {

    int result = 0;

    while (b != 0) {

        if ((b & 1) != 0) {

            result = add(result, a);

        }

        a <<= 1;

        b >>>= 1; // 使用逻辑右移,对于负数也适用

    }

    return result;

}

4)除法

除法是乘法的逆运算。我们可以找到商,通过不断从被除数中减去除数的倍数来实现。

public int divide(int dividend, int divisor) {

    if (divisor == 0) {

        throw new ArithmeticException("Cannot divide by zero");

    }

    if (dividend == Integer.MIN_VALUE && divisor == -1) {

        // 特殊情况处理,避免溢出

        return Integer.MAX_VALUE;

    }

   

    int result = 0;

    int sign = (dividend < 0) ^ (divisor < 0) ? -1 : 1; // 判断结果的符号

   

    // 将被除数和除数都转换为正数

    dividend = Math.abs(dividend);

    divisor = Math.abs(divisor);

   

    while (dividend >= divisor) {

        int tempDivisor = divisor;

        int quotient = 1;

        while ((tempDivisor << 1) <= dividend) {

            tempDivisor <<= 1;

            quotient <<= 1;

        }

       

        dividend -= tempDivisor;

        result = add(result, quotient);

    }

   

    return sign == -1 ? subtract(0, result) : result;

}

在所有这些函数中,我们必须处理溢出的情况,尤其是当涉及到最小整数值的时候。除法中有一个特殊情况,当最小值除以-1时,会产生溢出,因为其绝对值比Integer.MAX_VALUE大1,所以需要特殊处理。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/568347.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

C语言学习/复习29--内存操作函数memcpy/memmove/memset/memcmp

一、内存操作函数 1.memcpy()函数 注意事项1&#xff1a;复制的数目以字节为单位 注意事项2&#xff1a;一定要保证有足够空间复制 模拟实现1 拷贝字符案例&#xff1a;由于拷贝时函数本事就以字节为单位拷贝所以该例子也可用于其他类型数据的拷贝。 模拟实现2 将自身的…

diffusion model 简单demo

参考自&#xff1a; Probabilistic Diffusion Model概率扩散模型理论与完整PyTorch代码详细解读 diffusion 简单demo 扩散模型之DDPM Diffusion model 原理剖析 张振虎-扩散概率模型 生成扩散模型漫谈&#xff08;一&#xff09;&#xff1a;DDPM 拆楼 建楼 核心公式和逻辑 …

自适应STFT及其在地震时间行程自动拾取中的应用【附MATLAB代码】

文章来源&#xff1a;微信公众号&#xff1a;EW Frontie 摘要 在本文中&#xff0c;首先&#xff0c;我们提出的方法来产生高分辨率的短时傅里叶变换&#xff0c;通过计算最佳瞬时窗口长度。其次&#xff0c;利用生成的时频图提取瞬时走时属性&#xff0c;实现地震同相轴走时的…

vmstat命令详解

一、参数信息 vmstat 命令是用于报告虚拟内存统计信息的工具&#xff0c;常用于 Unix/Linux 系统上。它可以提供关于系统资源使用情况的详细信息&#xff0c;包括 CPU、内存、虚拟内存、磁盘、系统调用等方面的统计数据。以下是常见的 vmstat 命令参数的详解&#xff1a; vms…

k8s学习(三十六)centos下离线部署kubernetes1.30(单主节点)

文章目录 服务器准备工作一、升级操作系统内核1 查看操作系统和内核版本2 下载内核离线升级包3 升级内核4 确认内核版本 二、修改主机名/hosts文件1 修改主机名2 修改hosts文件 三、关闭防火墙四、关闭SELINUX配置五、时间同步1 下载NTP2 卸载3 安装4 配置4.1 主节点配置4.2 从…

2024商业地产五一劳动节健康大会朋克养生市集活动策划方案

2024商业地产五一劳动节健康大会朋克养生市集&#xff08;带薪健康 快乐打工主题&#xff09;活动策划方案 活动策划信息&#xff1a; 方案页码&#xff1a;53页 文件格式&#xff1a;PPT 方案简介&#xff1a; 打工不养生 赚钱养医生 期待已久的五一假期&#xff0c; …

WebSocket的原理、作用、常见注解和生命周期的简单介绍,附带SpringBoot示例

文章目录 WebSocket是什么WebSocket的原理WebSocket的作用全双工和半双工客户端【浏览器】API服务端 【Java】APIWebSocket的生命周期WebSocket的常见注解SpringBoot简单代码示例 WebSocket是什么 WebSocket是一种 通信协议 &#xff0c;它在 客户端和服务器之间建立了一个双向…

开发环境中的调试视图(IDEA)

当程序员写完一个代码时必然要运行这个代码&#xff0c;但是一个没有异常的代码却未必满足我们的要求&#xff0c;因此就要求程序员对已经写好的代码进行调试操作。在之前&#xff0c;如果我们要看某一个程序是否满足我们的需求&#xff0c;一般情况下会对程序运行的结果进行打…

java泛型介绍

Java 泛型是 JDK 5 引入的一个特性&#xff0c;它允许我们在定义类、接口和方法时使用类型参数&#xff0c;从而使代码更加灵活和类型安全。泛型的主要目的是在编译期提供类型参数&#xff0c;让程序员能够在编译期间就捕获类型错误&#xff0c;而不是在运行时才发现。这样做提…

C语言学习/复习30--结构体的声明/初始化/typedef改名/内存对齐大小计算

一、自定义数据类型 二、结构体 1.结构体的定义&#xff08;与数组相对比&#xff09; 2.结构体全局/局部变量的定义 3.typedef对结构体改名 4.匿名结构体类型的声明 注意事项1&#xff1a; 匿名后必须立即创建结构体变量 、 5.结构体与链表节点定义 注意事项1&…

Python基础07-高级列表推导式和Lambda函数

在Python中&#xff0c;列表推导式和Lambda函数是处理数据的强大工具。本文将介绍如何使用嵌套列表推导式、带有条件的列表推导式、多可迭代对象的列表推导式、Lambda函数、在列表推导式中使用Lambda函数、用于展平嵌套列表的列表推导式、对元素应用函数、使用Lambda函数与Map和…

Arena-Hard:开源高质量大模型评估基准

开发一个安全、准确的大模型评估基准通常需要包含三个重要内容&#xff1a;1&#xff09;稳定识别模型的能力&#xff1b;2&#xff09;反映真实世界使用情况中的人类偏好&#xff1b;3&#xff09;经常更新以避免过拟合或测试集泄漏。 但传统的基准测试通常是静态的或闭源的&…

程序员缓解工作压力小技巧

文章目录 1. 规划时间和任务2. 学会放松和调节情绪3. 培养兴趣爱好4. 保持健康的生活方式总结 当面对程序员这样需要高度精神集中和持续创新的工作时&#xff0c;缓解工作压力是至关重要的。下面分享一些我个人的经验和方法&#xff0c;希望能对大家有所帮助&#xff1a; 1. 规…

如何让AI生成自己喜欢的歌曲-AI音乐创作的正确方式 - 第507篇

历史文章 AI音乐&#xff0c;8大变现方式——Suno&#xff1a;音乐版的ChatGPT - 第505篇 日赚800&#xff0c;利用淘宝/闲鱼进行AI音乐售卖实操 - 第506篇 导读 在使用AI生成音乐&#xff08;AI写歌&#xff09;的时候&#xff0c;你是不是有这样的困惑&#xff1a; &…

线性模型算法

简介 本文章介绍机器学习中的线性模型有关内容&#xff0c;我将尽可能做到详细得介绍线性模型的所有相关内容 前置 什么是回归 回归的就是整合&#xff0b;预测 回归处理的问题可以预测&#xff1a; 预测房价 销售额的预测 设定贷款额度 可以根据事物的相关特征预测出对…

模型部署的艺术:让深度学习模型跃入生产现实

模型部署的艺术&#xff1a;让深度学习模型跃入生产现实 1 引言 1.1 部署的意义&#xff1a;为何部署是项目成功的关键 在深度学习项目的生命周期中&#xff0c;模型的部署是其成败的关键之一。通常&#xff0c;一个模型从概念构思、数据收集、训练到优化&#xff0c;最终目的…

【UML建模】用例图

1 参与者 参与者的概念&#xff1a; 指系统以外的、需要使用系统或与系统交互的外部实体 可以分为&#xff1a;人、外部设备、外部系统 参与者的图形符号&#xff1a; 例 3.1 在一个银行业务系统中&#xff0c;可能会有以下参与者 客户 &#xff1a;在银行业务系统中办理…

详细分析MySQL中的distinct函数(附Demo)

目录 前言1. 基本知识2. 基础Demo3. 进阶Demo 前言 该函数主要用于去重&#xff0c;对于细节知识&#xff0c;此文详细补充说明 1. 基本知识 DISTINCT 是一种用于查询结果中去除重复行的关键字 在查询数据库时&#xff0c;可能会得到重复的结果行&#xff0c;但有时只需要这…

奇妙的探索——偶然发现的bug

今天想在腾讯招聘官网找几个前端的岗位投一下&#xff0c;最近自己也在找工作&#xff0c;结果简历还没有投出去&#xff0c;就发现了腾旭招聘官网的3个前端bug。 1.有时候鼠标hover还没有滑倒下拉选框的菜单上&#xff0c;就消失了&#xff0c;消失的太快了&#xff0c;根本点…

揭秘App广告变现,如何轻松赚取额外收入?

揭秘App广告变现&#xff0c;如何轻松赚取额外收入&#xff1f; 在移动互联网高速发展的今天&#xff0c;APP广告变现已经成为了众多开发者和公司的主要盈利方式。但是&#xff0c;如何让一个APP实现高效的广告变现呢&#xff1f;这是一门大学问&#xff0c;需要我们用心去揣摩…