[史上最全]C#(VB.NET)中位运算符工作过程剖析(译)

原文地址CodeProject

目录

  • 介绍
  • “二进制-十进制”相互转换
    • 十进制->二进制
    • 二进制->十进制
  • OR运算符(按位或|)
    • OR运算符工作方式
    • FlagsAttribute
  • AND运算符(按位与&)
  • XOR运算符(按位异或^)
    • XOR运算符工作方式
    • 使用XOR交换两变量值的算法
    • 使用XOR加密
  • NOT运算符(按位非~)
  • 左移运算符(<<)
    • 左移运算符工作方式
    • 使用左移运算符计算2的幂
  • 右移运算符(>>)
    • 右移运算符工作方式
    • 使用右移运算符计算x/(2的幂)
  • 循环按位移动
    • 循环按位左移
    • 循环按位右移

介绍

在这篇博客中,我将来讨论与位操作符有关的内容。这篇文章中谈到的位操作符有:

  • OR(C#中使用“|”,VB.NET中使用Or)
  • AND(C#中使用“&”,VB.NET中使用And)
  • XOR(C#中使用“^”,VB.NET中使用Xor)
  • NOT(C#中使用“~”,VB.NET中使用Not)
  • 左移运算符(C#和VB.NET中都使用<<)
  • 右移运算符(C#和VB.NET中都使用>>)
  • 循环按位移动
    • 循环左移(C#和VB.NET中没有对应的运算符)
    • 循环右移(C#和VB.NET中没有对应的运算符)

位操作符一般用在数值类型上,它作用在数字二进制格式的每一位上(0和1),所以我们先要搞清楚十进制和二进制的相互转换。这篇文章开头我会给出一些(二进制-十进制)转换示例,虽然都是以Byte类型进行说明的,但其他诸如Int32、Int16等数值类型转换的原理是一样的。

位操作符的使用不仅仅只在C#和VB.NET两种语言中,本篇文章只以这两种语言举例。

“二进制-十进制”相互转换

这一节中我将介绍有关十进制与二进制相互转换的内容。

十进制->二进制

假设我们有一个十进制数字783,我们可以使用下面的方法将其转换成二进制:


除法:


783 / 2


391 / 2


195 / 2


97 / 2


48 / 2


24 / 2


12 / 2


6 / 2


3 / 2


1 / 2


:


391


195


97


48


24


12


6


3


1


0


余数:


1


1


1


1


0


0


0


0


1


1

当商为0时,我们停止计算。现在我们从右往左拼接每一步得到的余数,我们会得到1100001111。

按照下面方式可以将一个负十进制数转换为二进制(以-783为例):

  1. 先得到783的二进制:0000001100001111(前面空白补0)
  2. 按位取反得到:1111110011110000
  3. 然后加1得到:1111110011110001
  4. 那么,-783的二进制为1111110011110001
  5. 怎么确定得到的结果是一个负数呢?这主要依赖于数据类型。如果数据类型为Int16,那么第一位若为0,则为正数,否则为负数。如果数据类型是不带符号的,比如UInt16,那么第一位数不代表符号,1111110011110000就是十进制的64752。

二进制->十进制

如果你有一个二进制数字0000000100010110(Int16),现将每个位的顺序颠倒(你会得到0110100010000000),然后使用以下方法:


b:


0


1


1


0


1


0


0


0


1


0


0


0


0


0


0


0


b * 2n


0 * 20


1 * 21


1 * 22


0 * 23


1

* 24


0 * 25


0 * 26


0 * 27


1

*

28


0 * 29


0

* 210


0

* 211


0

* 212


0

* 213


0

* 214


0

* 215


结果:


0


2


4


0


16


0


0


0


256


0


0


0


0


0


0


0

最后将每一步得到的结果相加:

按照下面方式可以将一个负二进制数值转换成十进制(以1111111111010011为例):

  1. 将原数按位取反得到:0000000000101100
  2. 将取反后的结果转换成十进制:44
  3. 将44加1得到45
  4. 将45变为负数:-45
  5. 最后,负二进制数值1111111111010011的十进制格式为-45

OR运算符(按位或|

OR运算符工作方式

假设现在有两个Byte类型的数38和53,那么我们先将它们转换成二进制格式:

按照下表的方式:


A


0


0


1


0


0


1


1


0


B


0


0


1


1


0


1


0


1


A | B (A Or B)


0


0


1


1


0


1


1


1

如果两个数是Int16类型的,那么它们就有可能是负数。一个负数和一个正数按位或运算后得到的结果还是负数(第一位肯定是1),因此,-15|378(VB.NET中-15 Or 378)的结果为-5。

C#和VB.NET中的按位或运算符的使用,参见下面代码:

[C#]

[VB.NET]

FlagsAttribute

通过使用FlagsAttribute,你可以将枚举类型的每个值都当作二进制中的位(1和0),当然在定义枚举类型的时候有要求,即每个枚举值必须按照1、2、4、8(2的N次方)这样的规律初始化。

[C#]

[VB.NET]

现在你可以使用按位或运算符来操作枚举类型:

[C#]

[VB.NET]

AND运算符(按位与&

假设有两个数76和231,我们现将它们转换成二进制:

然后按照下表计算:


A


0


1


0


0


1


1


0


0


B


1


1


1


0


0


1


1


1


A & B (A And B)


0


1


0


0


0


1


0


0

仅仅当A和B都为负时,A&B(VB.NET中的A And B)的结果才为负,其他情况下结果都为正(只有A和B的第一位都为1时,结果的第一位才为1)。

C#和VB.NET中的按位与运算符的使用,参见下图:

[C#]

[VB.NET]

XOR运算符(按位异或^

XOR运算符的工作方式

按位或OR运算符不同于按位异或XOR运算符。如果你使用按位或OR,那么1|1(VB.NET中的1 Or 1)的结果为1,但是如果你使用按位异或XOR,那么1^1(VB.NET中的1 Xor 1)的结果为0。仅仅在1^0或者0^1时,结果才为1。

假设你有两个数值138和43,那么现将它们转换为二进制格式:

然后按照下表:


A


1


0


0


0


1


0


1


0


B


0


0


1


0


1


0


1


1


A ^ B (A Xor B)


1


0


1


0


0


0


0


1

C#和VB.NET中的按位异或运算符的使用,参见下面代码:

[C#]

[VB.NET]

使用XOR交换变量值的算法

使用XOR运算符可以交换两个变量值,并且不需要中间临时变量做辅助:

[C#]

[VB.NET]

使用XOR加密

在XOR运算符的帮助下,你可以给一个文本加密。遍历文本的每个字符,然后使用XOR运算符c ^ k(VB.NET中的c Xor k)生成新的字符。其中k就是一个整数值。

[C#]

[VB.NET]

最终输出结果为zFG]?G]?O?CK]]OIK。这种方式加密非常容易被破解,所以最好不要使用单一的字符(比如k),我们可以使用一串文本:

[C#]

[VB.NET]

最终的输出为m_?\ D+?.↓Z?\SL?Ka。现在破解这个加密算法相对来讲要复杂一些,但是这种方式还不是很保险,如果别人知道了你的key(代码中的k字符串),那么破解起来相当简单。因此,不要使用XOR这种方式作为加密的单一算法,如果你对安全、加密感兴趣,你可以结合其他的一些加密方式,将XOR运算符应用到其中,作为整个加密过程的一部分。

NOT运算符(按位非~

按位非操作符NOT将会改变二进制中每位的值,0变为1,1变为0。如果一个数值有符号,那么整数经过运算后会变成负数,负数经过变换后会变为正数。如果数值没有符号,那么永远都为正(0除外)。假设你有一个数值52(二进制00110100,Byte类型,无符号),那么~52(VB.NET中的Not 52)的计算方式为:


A


0


0


1


1


0


1


0


0


~A


1


1


0


0


1


0


1


1

将11001011转换成十进制为203,所以~52(Byte类型)的值为203。

C#和VB.NET中按位非运算符的使用,参见下面代码:

[C#]

[VB.NET]

左移运算符(<<

左移运算符工作方式

x<<n表示将X的二进制格式中的每位向左移动n个位置,右边空出来的位置补0。

如图所示,每位均向左移动1个位置,右边空出来的位置补0。所以154<<1等于52。154<<n的值参见下表:


154 << 0 (= 154)


1


0


0


1


1


0


1


0


154 << 1


0


0


1


1


0


1


0


0


154 << 2


0


1


1


0


1


0


0


0


154 << 3


1


1


0


1


0


0


0


0


154 << 4


1


0


1


0


0


0


0


0


154 << 5


0


1


0


0


0


0


0


0


154 << 6


1


0


0


0


0


0


0


0


154 << 7


0


0


0


0


0


0


0


0


154 << 8


0


0


0


0


0


0


0


0

C#和VB.NET中左移运算符的使用,参见下面代码:

[C#]

[VB.NET]

使用左移运算符计算2的幂

1<<n的值为(2的n次方),但是使用这种方式计算2的幂要比使用Math.Pow更快:

[C#]

[VB.NET]

右移运算符(>>

右移运算符工作方式

x>>n表示将x的二进制格式的每位均向右移动n个位置,左边空出来的位置补0(与左移相反)。

如上图所示,每位均向右移动1个位置。所以155>>1的值为77。注意如果为负数,那么它的符号会被隐藏掉。

下表显示的是计算155>>n的值:


155 >> 0


1


0


0


1


1


0


1


1


155 >> 1


0


1


0


0


1


1


0


1


155 >> 2


0


0


1


0


0


1


1


0


155 >> 3


0


0


0


1


0


0


1


1


155 >> 4


0


0


0


0


1


0


0


1


155 >> 5


0


0


0


0


0


1


0


0


155 >> 6


0


0


0


0


0


0


1


0


155 >> 7


0


0


0


0


0


0


0


1


155 >> 8


0


0


0


0


0


0


0


0

C#和VB.NET中的右移运算符的使用,参见下面代码:

[C#]

[VB.NET]

使用右移运算符计算x/(2的幂)

x>>n的值等于x/(2的n次方),比如8>>2的值为8/(2的2次方),也就是8/4。

[C#]

[VB.NET]

当然,这种方式的计算速度也要高于8/Math.Pow(2,2);

[C#]

[VB.NET]

循环按位移动

循环按位左移

循环按位左移会将数值的二进制格式中的每位均向左移动1个位置,然后将移出来的数值(1或0)替补到右边空白处。

上图显示了将154循环按位向左移动1位,它的值等于154<<1|154>>7。循环按位左移得到的结果可以归纳为:a<<n|a>>(b-n)。b为数值的位数,如果数值为Byte类型,那么最后的结果为a<<n|a>>(8-n),如果数值为Int32类型,那么b为32,最后的结果为a<<n|a>>(32-n)。

C#和VB.NET中循环按位左移的使用,可以参见下面:

[C#]

[VB.NET]

循环按位右移

循环按位右移会将数值的二进制格式的每位均向右移动1个位置,然后将移出来的数值(1或0)替补到左边空白处。

如上图所示,将155循环按位右移1个位置,最后它的值等于155>>1|155<<7。循环按位右移得到的结果可以归纳为:a>>n|a<<(b-n)。其中b为数值位数。如果数值为Byte类型,那么结果为a>>n|a<<(8-n),如果数值为Int32类型,那么得到的结果为a>>n|a<<(32-n)。

C#和VB.NET中循环按位右移的使用,可以参见下面代码:

[C#]

[VB.NET]

译者注:在使用位操作符时,一定要先确定被操作的数值是什么类型,占多少位,同一个数值,数据类型不同,最后得到的结果不一样。原文中,对于任何一个数值(比如52),都在强调它是Byte类型还是Int16类型。

时间: 2024-10-24 12:10:35

[史上最全]C#(VB.NET)中位运算符工作过程剖析(译)的相关文章

webpack之前端性能优化(史上最全,不断更新中。。。)

最近在用webpack优化首屏加载性能,通过几种插件之后我们上线前后的速度快了一倍,在此就简单的分享下吧,先上个优化前后首屏渲染的对比图. 可以看到总下载时间从3800ms缩短到1600ms. 我们在用webpack时一般都会选择多入口文件吧,为的就是将自己的源码跟第三方库代码分离.这是之前的代码, entry: { entry: './src/main.js', vendor: ['vue', 'vue-router', 'vuex', 'element-ui','echarts'] }, o

史上最全的LaTeX特殊符号语法

史上最全的LaTeX特殊符号语法 运算符 语法 效果 语法 效果 语法 效果 + \(+\) - \(-\) \triangleleft \(\triangleleft\) \pm \(\pm\) \div \(\div\) \triangleright \(\triangleright\) \times \(\times\) \setminus \(\setminus\) \star \(\star\) \cdot \(\cdot\) \cap \(\cap\) \ast \(\ast\) \

.Net魔法堂:史上最全的ActiveX开发教程——ActiveX与JS间交互篇

一.前言 经过上几篇的学习,现在我们已经掌握了ActiveX的整个开发过程,但要发挥ActiveX的真正威力,必须依靠JS.下面一起来学习吧! 二.JS调用ActiveX方法 只需在UserControl子类中(即自定义的ActiveX控件中),编写公共方法即可. C# [Guid("0203DABD-51B8-4E8E-A1EB-156950EE1668")] public partial class Uploader : UserControl, IObjectSafety { p

史上最全最常用的正则表达式-(基本够用值得收藏)

一.校验数字的表达式 1 数字:^[0-9]*$ 2 n位的数字:^\d{n}$ 3 至少n位的数字:^\d{n,}$ 4 m-n位的数字:^\d{m,n}$ 5 零和非零开头的数字:^(0|[1-9][0-9]*)$ 6 非零开头的最多带两位小数的数字:^([1-9][0-9]*)+(.[0-9]{1,2})?$ 7 带1-2位小数的正数或负数:^(\-)?\d+(\.\d{1,2})?$ 8 正数.负数.和小数:^(\-|\+)?\d+(\.\d+)?$ 9 有两位小数的正实数:^[0-9]

史上最全的maven的pom.xml文件详解

史上最全的maven的pom.xml文件详解 http://www.cnblogs.com/hafiz/p/5360195.html <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 h

史上最全开源大数据工具汇总

摘要 史上最全的开源大数据工具,非常实用,请务必收藏! 史上最全的开源大数据工具,非常实用,请务必收藏! 查询引擎 Phoenix Salesforce公司出品,Apache HBase之上的一个SQL中间层,完全使用Java编写 Stinger 原叫Tez,下一代Hive, Hortonworks主导开发,运行在YARN上的DAG计算框架 Presto Facebook开源 Spark SQL Spark上的SQL执行引擎 Pig 基于Hadoop MapReduce的脚本语言 Clouder

史上最全、最深、最细JAVA300集全套39.4G视频课程免费分享

尚学堂JAVA300集大型基础视频课程(史上最全.最深.最细) 中国首套大型JAVA技术视频 [已更新完至300集] 还在为如何学习JAVA技术而迷茫吗?还在为如何学习Android技术而迷茫吗? java300集大型基础视频课程,现已发布第三季更新至(300)集. 史上最全:覆盖工作中需要的所有技术点. 史上最细:讲师授课代码全部手工敲出,真正的手把手教授您如何编程. 史上最深:我们的基础视频也深刻的从内存结构进行分析.JDK源代码进行分析,这都是高手的必备技能.高手从入门抓起.入门时培养良好

史上最全PHP正则表达式实例汇总

收集了一份php正则表达式的实例教程,真心不错,记录下. 正则表达式用于字符串处理.表单验证等场合,实用高效. 一些常用的表达式: $str = preg_replace("/(<a.*?>)(.*?)(<\/a>)/", '\1<span class="link">\2</span>\3', $str); 其中用了三个子模式(每个圆括号中内容为一个子模式),第一个是链接开始标签,第二个是链接文本,第三个是</a

史上最全: svn与git的对比(二):svn与git的相关概念

如图1是svn服务器端数据的目录结构 下面是git服务器端的目录结构 纵观svn和git服务端的目录结构我们很容易发现 1.有些文件夹还是蛮像的,甚至是一样的比如说svn中的conf,hooks等git中也有,svn中的db类似与git中的objects. 2.git中的内容好像是比svn中,主要是多了head文件,branches,refs等 下面我们就详细来看一下这些文件夹或文件的作用 史上最全: svn与git的对比(二):svn与git的相关概念