热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

基础位运算基本原理和应用

微信公众号位运算是编程语言的基础,在看源码的时候会看到很多位运算代码,但是在项目代码中很少会看到位运算。因为应用代码中,有很多判断和计

微信公众号

基础位运算基本原理和应用
位运算是编程语言的基础,在看源码的时候会看到很多位运算代码,但是在项目代码中很少会看到位运算。因为应用代码中,有很多判断和计算都可以直接用数值的判断和计算完成,没有必要去用位运算,以至于这些基础的东西慢慢用的越来越少,慢慢也就忘了。导致的一个结果就是看源代码很费力,因为大量的位运算逻辑,看不懂。
作为程序员感觉数据位运算是非常必要,有点如下:

  • 看源码时能够更好的理解
  • 位运算更接近计算机的习惯,执行的效率会更高
  • 装逼利器,在项目中使用位运算,体现逼格

N种基本的位运算

位运算 -- 与运算符(&)

运算规则:

操作数1 0 0 1 1
操作数2 0 1 0 1
与运算 0 0 0 1

规则总结:只有两个操作数都是1的时候运算结果才为1,其他都是0。换句话说就是只要包含0就是0。

运算如下:

基础位运算基本原理和应用

位运算 -- 或运算符(|)

运算规则:

操作数1 0 0 1 1
操作数2 0 1 0 1
或运算 0 1 1 1

规则总结:只有两个操作数都是0的时候运算结果才为0,其他都是1。换句话说就是只要包含1就是1。

运算如下:

基础位运算基本原理和应用

位运算 -- 非运算符(~)

运算规则:

操作数 1 0
或运算 0 1

规则总结:取反操作,1变0,0变1。

位运算 -- 异或运算符(^)

运算规则:

操作数1 0 0 1 1
操作数2 0 1 0 1
或运算 0 1 1 0

规则总结:操作数相同,运算结果就是0,反之操作数不同就为1。
基础位运算基本原理和应用

位运算 -- 有符号位移运算符(>>,<<)

在二进制里面总共有32位,0-31,第31位是表示当前数值的正负,当时0的时候表示这个数值是正数,当是1表示这个数值是负数。

正数左移(<<)

以2<<2为例。
2:00000000 00000000 00000000 00000010
向左移动两位,右侧会空出来两个位置,两个位置用0补位得到的结果如下:
8:00000000 00000000 00000000 00001000
转换成十进制对应的数值为8。因此可以得到2<<2的结果是8。

负数左移(<<)

以-2<<2为例。
-2:11111111 11111111 11111111 11111110
向左移动两位,右侧空出的两个位置用0补位,到这里还没有结束,要是想计算出它的值,还要做补位,那就是将当前移位得到的结果(11111111 11111111 11111111 11111000)减一后再取反码,得的结果就是补码的结果。
减一操作:
11111111 11111111 11111111 11110111
取反码的结果:
00000000 00000000 00000000 00001000
这个对应的值是8,因为是负值,那么得到的结果就是-8,因此-2<<2的结果是-8。

左移总结

这里可以看出来,在左移的时候,不论这个目标值(2或-2)是正数还是负数,结果都符合一个规律,即表达式:$m*2^n$。m表示目标值,n表示的是移位的位数。
-2<<2 = -8,2<<2 = 8,同理可以得到2<<4 = 2x2^4 = 32,-2<<4 = -2x2^4 = -32。

正数右移(>>)

以10>>2为例。
10:00000000 00000000 00000000 00001010
右移两位,右侧的10就会被舍弃,左侧会空出两个空位,空位用符号位补齐,前面说过正数的符号位是0,也就是用0补齐,得到的结果如下:
2:00000000 00000000 00000000 00000010
得到的十进制结果是2,结论就是10>>2=2。

负数右移(>>)

以-10>>2为例。
-10:11111111 11111111 11111111 11110110
同样是右移,溢出的部分舍弃,空位的部分用符号位补齐,得到的结果如下:
11111111 11111111 11111111 11111101
减一操作后结果:
11111111 11111111 11111111 11111100
取反码后的结果:
00000000 00000000 00000000 00000011
对应的十进制结果是3,因为是负数,那么结果就是-3。结论是-10>>2。

有符号位移总结

整体的总结来看,正数的左移和右移没有什么问题,但是负数存在一个补码的问题,比较麻烦。补码记住一个口诀就可以,移位、补码、减一、取反码,得到正数结果加个负号。这样就可以得到正确结果。

位运算 -- 无符号位移运算符(>>>)

Java中没有无符号左移的说法,这里只说右移。同样也是分正数和负数来讲。

正数右移(>>>)

以10>>>2为例。
10:00000000 00000000 00000000 00001010
右移后,左侧空出的位置用0补齐,但是这里需要注意的是这个0并不是指符号位,只是一个普通的补位。得到的结果如下:
2:00000000 00000000 00000000 00000010
得到的十进制结果是2,结论就是10>>2=2。这个和有符号位移是得到相同的结果。

负数右移(>>>)

以-10>>>2为例。
-10:11111111 11111111 11111111 11110110
右移后,左侧空位用0补,注意不是用1补,后面说原因。
00111111 11111111 11111111 11111101
这个结果就很大了,结果是1073741821,负数变成了这么大的负数,不要怀疑自己的眼神,这个结果是正确的。

总结

所谓的无符号右移,就是将原有的二进制值直接右移得到结果,不论是负数还是正数,没有补码的操作,补位都统一使用0,而不是对应的符号位1或0。
到这里N种基本的位运算已经说明结束,接下来看几个例子,和在代码中常用的一些技巧。

常用位运算使用
  • 判断一个数是奇数还是偶数。
    // true表示为奇数,false表示为偶数
    public boolean checkNum(int num){
    return (num&1) == 1;
    }

    1的二进制是00000000 00000000 00000000 00000001,&运算的规则是只有都是1,结果才是1,很明显,不管是什么数,和1进行&运算,前31位都是0,只有最后一位可能得出不同的结果。偶数最后一位肯定是0,奇数肯定是1,那么结果就显而易见了。

  • 不用中间变量,交换两个数值(面试常见)
    // 传入的a=3,b=5
    public void swap(int a,int b){
    a ^= b;// a = a^b;
    b ^= a;// b = b^a;
    a ^= b;// a = a^b;
    System.out.println("a="+a);
    System.out.println("b="+b);
    }
    /*
    输出结果:
    a=5
    b=3
    */

    这个灵活的用到了异或来实现。下面做个简单的说明。
    a = 3:00000011
    b = 5:00000101
    a异或b得到的结果是:00000110,这个结果赋值给a;
    b异或a得到的结果是:00000011,这个结果赋值给b;
    a异或b得到的结果是:00000101,这个结果赋值给a;
    所以最终得到的结果是:
    a = 5:00000101
    b = 3:00000011

  • 取绝对值
    public int getAbs(int n){
    return (n ^ (n >> 31)) - (n >> 31);
    }

    具体不说,可以自己尝试着写一下。(因为涉及到正数、负数的举例,然后负数又有补码操作,要写的内容太多,本博主就傲娇一下,不写啦!)
    其实这里的例子很多,就不都去说了,有兴趣的可以度娘一下相关的博客,有很多文章。

实际项目经验操作

涉及到公司的东西,这里不会写的很具体,给思路。

现在有一个订单,订单可以进行很多操作,如:创建、修改、退单、接单、终止、派单、提交完成等操作。同时订单也有很多状态,如:待接单、进行中、已终止、已完成等状态。现在的场景就是不同的状态对应的操作是不一样的,前端需要根据不同的状态显示不同的操作控件。

思路一:

最简单也是最麻烦的,用N个flag字段表示可进行的操作,返回数据的时候对每个flag字段赋值true、false,表示是否能够进行此操作。但是这样存在一个巨大的问题,当后期有一个操作去掉了或者添加了几种新的操作,这个时候就涉及到字段的删除和新增,改动较大,可维护性差,不够灵活。

思路二:(推荐)

使用此二进制的方式表示。实现如下:

第一位:0,1表示是否可以修改

第二位:0,1表示是否可以接单

第三位:0,1表示是否可以终止

返回给前端只要一个字段tags,如对应的值是:010,那就表示不可以修改、可以接单、不可以终止。

如果现在终止操作不要了,那么这个二进制位始终给0就可以。如果新增了一个提交操作,那就让第四位表示是否可以提交。这样不需要添加字段,只要在原字段加一位就OK。

另外还有其他很多应用场景。


推荐阅读
  • Spring框架入门指南:专为新手打造的详细学习笔记
    Spring框架是Java Web开发中广泛应用的轻量级应用框架,以其卓越的功能和出色的性能赢得了广大开发者的青睐。本文为初学者提供了详尽的学习指南,涵盖基础概念、核心组件及实际应用案例,帮助新手快速掌握Spring框架的核心技术与实践技巧。 ... [详细]
  • 开发心得:深入探讨Servlet、Dubbo与MyBatis中的责任链模式应用
    开发心得:深入探讨Servlet、Dubbo与MyBatis中的责任链模式应用 ... [详细]
  • 2019年后蚂蚁集团与拼多多面试经验详述与深度剖析
    2019年后蚂蚁集团与拼多多面试经验详述与深度剖析 ... [详细]
  • 本文详细探讨了Java集合框架的使用方法及其性能特点。首先,通过关系图展示了集合接口之间的层次结构,如`Collection`接口作为对象集合的基础,其下分为`List`、`Set`和`Queue`等子接口。其中,`List`接口支持按插入顺序保存元素且允许重复,而`Set`接口则确保元素唯一性。此外,文章还深入分析了不同集合类在实际应用中的性能表现,为开发者选择合适的集合类型提供了参考依据。 ... [详细]
  • HBase客户端Table类中getRpcTimeout方法的应用与编程实例解析 ... [详细]
  • 本文作为“实现简易版Spring系列”的第五篇,继前文深入探讨了Spring框架的核心技术之一——控制反转(IoC)之后,将重点转向另一个关键技术——面向切面编程(AOP)。对于使用Spring框架进行开发的开发者来说,AOP是一个不可或缺的概念。了解AOP的背景及其基本原理,对于掌握这一技术至关重要。本文将通过具体示例,详细解析AOP的实现机制,帮助读者更好地理解和应用这一技术。 ... [详细]
  • 本文详细解析了如何使用 jQuery 实现一个在浏览器地址栏运行的射击游戏。通过源代码分析,展示了关键的 JavaScript 技术和实现方法,并提供了在线演示链接供读者参考。此外,还介绍了如何在 Visual Studio Code 中进行开发和调试,为开发者提供了实用的技巧和建议。 ... [详细]
  • 本文将详细介绍在Android应用中添加自定义返回按钮的方法,帮助开发者更好地理解和实现这一功能。通过具体的代码示例和步骤说明,本文旨在为初学者提供清晰的指导,确保他们在开发过程中能够顺利集成返回按钮,提升用户体验。 ... [详细]
  • 如何将Java 8中的嵌套列表 List<List<Integer>> 转换为单一列表 List<Integer> 的操作方法
    本文详细探讨了如何在Java 8中将嵌套列表 `List` 展平为单一列表 `List` 的方法。通过使用流(Stream)API 和 `flatMap` 操作,可以高效地实现这一转换过程。该技术在处理多层数据结构时非常实用,适用于多种应用场景,如数据聚合和简化复杂列表操作。文章提供了详细的代码示例和解释,帮助开发者更好地理解和应用这一技术。 ... [详细]
  • 修复一个 Bug 竟耗时两天?真的有那么复杂吗?
    修复一个 Bug 竟然耗费了两天时间?这背后究竟隐藏着怎样的复杂性?本文将深入探讨这个看似简单的 Bug 为何会如此棘手,从代码层面剖析问题根源,并分享解决过程中遇到的技术挑战和心得。 ... [详细]
  • Java新手求助:如何优雅地向心仪女生索要QQ联系方式(附代码示例与技巧)
    在端午节后的闲暇时光中,我无意间在技术社区里发现了一篇关于如何巧妙地向心仪女生索取QQ联系方式的文章,顿时感到精神焕发。这篇文章详细介绍了源自《啊哈!算法》的方法,不仅图文并茂,还提供了实用的代码示例和技巧,非常适合 Java 新手学习和参考。 ... [详细]
  • Java 8 引入了 Stream API,这一新特性极大地增强了集合数据的处理能力。通过 Stream API,开发者可以更加高效、简洁地进行集合数据的遍历、过滤和转换操作。本文将详细解析 Stream API 的核心概念和常见用法,帮助读者更好地理解和应用这一强大的工具。 ... [详细]
  • 提升工作效率:掌握这些技巧,IDEA 使用效率翻倍 | IDEA 高效操作指南
    提升工作效率:掌握这些技巧,IDEA 使用效率翻倍 | IDEA 高效操作指南 ... [详细]
  • 利用Redis HyperLogLog高效统计微博日活跃和月活跃用户数
    本文探讨了如何利用Redis的HyperLogLog数据结构高效地统计微博平台的日活跃用户(DAU)和月活跃用户(MAU)数量。通过HyperLogLog的高精度和低内存消耗特性,可以实现对大规模用户数据的实时统计与分析,为平台运营提供有力的数据支持。 ... [详细]
  • 深入解析:JavaScript中的表达式与语句有何不同
    深入解析:JavaScript中的表达式与语句有何不同 ... [详细]
author-avatar
没有结局的梦z最痛
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有