了解位运算符的工作原理(C# 和 VB.NET 示例)






4.97/5 (147投票s)
本文解释了位运算符的工作原理,并结合 C# 和 VB.NET 中的示例介绍它们的几种用途。
关于下载文件
下载的文件包含一个 Visual Studio 2012 项目和源文件,但没有可执行文件。如果您不使用 Visual Studio,或者您使用的版本不同且项目无法加载,请尝试打开 Program.cs (C#) 或 Program.vb (VB.NET)。下载文件中包含 FlagsAttribute、XOR 加密、XOR 交换算法等示例。Main
方法是空的。
目录
引言
在本文中,我将向您介绍位运算符的工作原理。
本文中的所有运算符
- OR(包含 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 中无运算符)
位运算符用于处理数字。位运算符会作用于数字的位,因此如果您想理解位运算符的工作原理,则应首先学会十进制与二进制的相互转换。我在十进制与二进制相互转换部分讲解了如何进行转换。在本文中,我主要使用 Byte
类型进行示例。但这些示例也适用于其他类型,如 Int32
或 Int16
。
位运算符在 C# 和 VB.NET 等多种语言中使用,但在本文中,我将提供 C# 和 VB.NET 的示例。
十进制与二进制相互转换
当您使用位运算符时,整数的二进制形式中的每一位都会执行一个操作。例如,110100112 等于 21110(下标数字表示数字的基数)。而 14310 等于 100011112。在本段中,我将介绍如何将十进制转换为二进制以及如何将二进制转换为十进制。
十进制转二进制
如果我们有一个十进制数,例如 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**。
将 78310 转换为二进制数的逐步解释
- 将 783 除以 2。
- 商为 391,余数为 1。
- 写下余数:1。
- 将 391 除以 2。
- 商为 195,余数为 1。
- 写下余数:1
- 将 195 除以 2
- 商为 97,余数为 1。
- 写下余数:1
- 将 97 除以 2
- 商为 48,余数为 1。
- 写下余数:1。
- 将 48 除以 2。
- 商为 24,余数为 0。
- 写下余数:0。
- 将 24 除以 2。
- 商为 12,余数为 0。
- 写下余数:0。
- 将 12 除以 2。
- 商为 6,余数为 0。
- 写下余数:0。
- 将 6 除以 2。
- 商为 3,余数为 0。
- 写下余数:0。
- 将 3 除以 2。
- 商为 1,余数为 1。
- 写下余数:1。
- 将 1 除以 2。
- 商为 0,余数为 1。
- 写下余数:1。
- 现在,停止除法,因为商为 0。
- 从 **右向左** 阅读余数序列 (1111000011),您将读到 1100001111。因此,11000011112 等于 78310。
- 现在,您可以添加/删除前导零。如果您的数据类型是 Int16,则需要 16 位。如果您的数据类型是 Int32,则需要 32 位。783 是一个 Int16,因此我们在前面添加零,直到有 16 位:0000001100001111。
将负十进制数转换为二进制(例如 -783)
- 取 783 的二进制形式:**0000001100001111**。
- 反转它:**1111110011110000**
- 将 **1111110011110000** 加上 1。
- 因此,-78310 等于 **11111100111100012**。
- 如何确定这个数字是负数?这取决于数据类型。如果数据类型是 Int16,则第一个位是 0,则数字为正。如果第一个位是 1,则数字为负。因此,1111110011110000 (Int16) 是 -783,但对于无符号数字,例如
UInt16
,第一个数字 **不** 表示数字是否为负。例如,对于 UInt16,我们可以确定它是正数,因为它无符号。因此,1111110011110000 作为UInt16
等于 64752。
二进制转十进制
如果您有一个二进制数 0000000100010110 (Int16 -> 第一个数字 = 0,正数),则反转位的顺序(您将得到 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 |
---|
现在,您需要将所有结果相加。
0 + 2 + 4 + 16 + 0 + 0 + 0 + 256 + 0 + 0 + 0 + 0 + 0 + 0 + 0 = 2 + 4 + 16 + 256 = 278
将 00000001000101102 转换为十进制数的逐步解释
- 反转二进制数中的位顺序(同时改变第一个 0(正号)的位置):0000000100010110 -> **0110100010000000**。
- (0110100010000000) 的第一个位:0。
- 0 * 20 = **0**,所以写 0。
- 取下一位:1。
- 1 * 21 = **2**,所以写 2。
- 取下一位:1。
- 1 * 22 = **4**,所以写 4。
- 取下一位:0。
- 0 * 23 = **0**,所以写 0。
- 取下一位:1。
- 1 * 24 = **16**,所以写 16。
- 取下一位:0。
- 0 * 25 = **0**,所以写 0。
- 取下一位:0。
- 0 * 26 = **0**,所以写 0。
- 取下一位:0。
- 0 * 27 = **0**,所以写 0。
- 取下一位:1。
- 1 * 28 = **256**,所以写 256。
- 取下一位:0。
- 0 * 29 = **0**,所以写 0。
- 取下一位:0。
- 0 * 210 = **0**,所以写 0。
- 取下一位:0。
- 0 * 211 = **0**,所以写 0。
- 取下一位:0。
- 0 * 212 = **0**,所以写 0。
- 取下一位:0。
- 0 * 213 = **0**,所以写 0。
- 取下一位:0。
- 0 * 214 = **0**,所以写 0。
- 取下一位:0。
- 0 * 215 = **0**,所以写 0。
- 这是最后一位,现在将您写下的所有数字相加:**0 + 2 + 4 + 0 + 16 + 0 + 0 + 0 + 256 + 0 = 2 + 4 + 16 + 256 = 278**,因此 **1000101102** 等于 **27810**。
将负二进制数转换为十进制数的方法(1111111111010011 = Int16 -> 第一位 = 1,因此为负数)
- 反转二进制数:(1111111111010011 -> **0000000000101100**)
- 将 0000000000101100 转换为十进制数:44。
- 将 44 加上 1:45。
- 将 45 变为负数:-45。
- 因此,负二进制数 1111111111010011 等于十进制数 **-45**。
OR 运算符(包含 OR)
OR 运算符的工作原理
如果您有两个数字,例如 38 (Byte
) 和 53 (Byte
),我们首先将这些数字转换为二进制:
38 -> 00100110
53 -> 00110101
- 现在,我们取 38 (A) 的第一位和 53 (B) 的第一位。A = 0 且 B = 0。
- 如果 A 为 1,B 为 1,或者两者都为 1,则写 1。如果两者都为 0,则写 0。
- A 和 B 都为 0,因此写 **0**。
- 取下一位。现在,A = 0 且 B = 0。
- A 和 B 都为 0,因此写 **0**。
- 取下一位。现在,A = 1 且 B = 1。
- A 和 B 都为 1,因此写 **1**。
- 取下一位。现在,A = 0 且 B = 1。
- A 为 0,但 B 为 1,因此写 **1**。
- 取下一位。现在,A = 0 且 B = 0。
- A 和 B 都为 0,因此写 **0**。
- 取下一位。现在,A = 1 且 B = 1。
- A 和 B 都为 1,因此写 **1**。
- 取下一位。现在,A = 1 且 B = 0。
- B 为 0,但 A 为 1,因此写 **1**。
- 取下一位。现在,A = 0 且 B = 1。
- A 为 0,但 B 为 1,因此写 **1**。
- 这是所有写入数字的序列:00110111。
- 如果您将 00110111 转换为十进制数,您将得到 **55**。因此,
38 | 53
(VB.NET 中为38 Or 53
) 等于 **55**。
此方法的对应表:
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
,例如,那么其中一个数字可能是负数。如果第一个 Int16
的符号位是 0(正数),第二个数字的符号位是 1(负数),那么结果的符号位将是 **1**。因此,-15 | 378
(VB.NET 中为 -15 Or 378
) 等于 **-5**。
C# 和 VB.NET 的 OR 运算符代码
byte inclusiveOrExample = 38 | 53; // change 'byte' into the data type which you need
Dim inclusiveOrExample As Byte = 38 Or 53 ' change "byte" into the data type which you need
FlagsAttribute
您可以使用 FlagsAttribute[^] 将枚举视为位字段。在带有 FlagsAttribute
的枚举中,您应该将第一个值设置为 1,第二个值设置为 2,第三个值设置为 4,第四个值设置为 8... 将“None”设置为 0。
[Flags]
public enum Priority
{
None = 0,
VeryLow = 1,
Low = 2,
Medium = 4,
High = 8,
VeryHigh = 16
}
<Flags>
Public Enum Priority
None = 0
VeryLow = 1
Low = 2
Medium = 4
High = 8
VeryHigh = 16
End Enum
现在,您可以使用 OR 运算符组合枚举值。
Priority p = Priority.Medium | Priority.High;
Console.WriteLine(p.ToString());
// output: Medium, High
Dim p As Priority = Priority.Medium Or Priority.High
Console.WriteLine(p.ToString())
' output: Medium, High
输出将是 Medium, High
,因为我使用的是 FlagsAttribute
。如果我删除 [Flags]
(VB.NET 中为 <Flags>
),则输出将是 12
。重要提示:如果您还想声明一个值“MediumHigh”,那么“MediumHigh”的值应该是 Medium | High
(VB.NET 中为 Medium Or High
)。
AND 运算符
如果您有两个数字,例如 76 和 231,我们首先将这些数字转换为字节:
76 -> 01001100
231 -> 11100111
- 现在,我们取 76 (A) 的第一位和 231 (B) 的第一位。A = 0 且 B = 1。
- 如果 A 和 B 都为 1,则写 1。否则,写 0。
- A 不为 1,因此写 **0**。
- 取下一位。现在,A = 1 且 B = 1。
- A 和 B 都为 1,因此写 **1**。
- 取下一位。现在,A = 0 且 B = 1。
- A 不为 1,因此写 **0**。
- 取下一位。现在,A = 0 且 B = 0。
- A 和 B 都为 0,因此写 **0**。
- 取下一位。现在,A = 1 且 B = 0。
- B 不为 1,因此写 **0**。
- 取下一位。现在,A = 1 且 B = 1。
- A 和 B 都为 1,因此写 **1**。
- 取下一位。现在,A = 0 且 B = 1。
- A 不为 1,因此写 **0**。
- 取下一位。现在,A = 0 且 B = 1。
- A 不为 1,因此写 **0**。
- 这是所有写入位的序列:01000100。
- 将 01000100 转换为十进制数:68。
- 因此,
76 & 231
(VB.NET 中为76 And 231
) 等于 **68**。
此方法的对应表
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
(A And B
in VB.NET) 将为负数;否则,A & B
(A And B
) 将为正数。
C# 和 VB.NET 的 AND 运算符实现
byte andOperatorExample = 76 & 231; // change 'byte' into the data type which you need
Dim andOperatorExample As Byte = 76 And 231 ' change "Byte" into the data type which you need
XOR 运算符(异或)
XOR 运算符的工作原理
异或运算符与包含 OR 运算符不同。如果您使用包含 OR,1 | 1
(VB.NET 中为 1 Or 1
) 等于 1
。但如果您使用 XOR 运算符,1 ^ 1
(VB.NET 中为 1 Xor 1
) 不等于 1
,而是等于 0
。只有 0 ^ 1
(VB.NET 中为 0 Xor 1
) 和 1 ^ 0
(VB.NET 中为 1 Xor 0
) 返回 1
。
如果您有两个数字,例如 138 和 43,则使用以下方法计算 138 ^ 43
(VB.NET 中为 138 Xor 43
):
-
138 -> 10001010 43 -> 00101011
- 取 138 (A) 的第一位和 43 (B) 的第一位。A = 1 且 B = 0。
- A 为 1,B 为 0,因此写 **1**。
- 取下一位。现在,A = 0 且 B = 0。
- A 和 B 都为 0,因此写 **0**。
- 取下一位。现在,A = 0 且 B = 1。
- A 为 0,B 为 1,因此写 **1**。
- 取下一位。现在,A = 0 且 B = 0。
- A 和 B 都为 0,因此写 **0**。
- 取下一位。现在,A = 1 且 B = 1。
- A 和 B 都为 1,因此写 **0**。
- 取下一位。现在,A = 0 且 B = 0。
- A 和 B 都为 0,因此写 **0**。
- 取下一位。现在,A = 1 且 B = 1。
- A 和 B 都为 1,因此写 **0**。
- 取下一位。现在,A = 0 且 B = 1。
- A 为 0,B 为 1,因此写 **1**。
- 这是所有写入位的序列:10100001。
- 将 10100001 转换为十进制数,您将得到 161。
- 因此,
138 ^ 43
(VB.NET 中为138 Xor 43
) 等于 **161**。
此方法的对应表
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 的异或 (XOR) 运算符实现
byte exclusiveOrExample = 138 ^ 43 // change 'byte' into the data type which you need
Dim exclusiveOrExample As Byte = 138 Xor 43
XOR 交换算法
使用 XOR 交换算法[^],您可以 **不** 使用临时变量来交换两个变量(具有相同数据类型)的值。
int x = 31643; // you can choose another data type
int y = 134;
x ^= y;
y ^= x;
x ^= y;
Console.WriteLine(x);
Console.WriteLine(y);
// output: 134
// 31643
Dim x As Integer = 31643
' you can choose another data type
Dim y As Integer = 134
x = x Xor y
y = x Xor y
x = x Xor y
Console.WriteLine(x)
Console.WriteLine(y)
' output: 134
' 31643
XOR 加密
使用 XOR 运算符,您可以加密文本。迭代所有字符,新的(加密的)字符是 c ^ k
(VB.NET 中为 c Xor k
)。c
是当前字符的 int 值,k
是密钥的 int 值。
string msg = "This is a message.";
char k = '.'; // For example, use '.' as key. You can also use another key.
StringBuilder sb = new StringBuilder();
foreach (char c in msg)
{
sb.Append((char)(c ^ k));
}
Console.WriteLine(sb.ToString());
Dim msg As String = "This is a message."
Dim k As Char = "."C
' For example, use '.' as key. You can also use another key.
Dim sb As New StringBuilder()
For Each c As Char In msg
sb.Append(ChrW(AscW(c) Xor AscW(k)))
Next
Console.WriteLine(sb.ToString())
输出为 zFG]♫G]♫O♫CK]]OIK
。使用 频率分析[^] 可以轻松破解此加密。因此,请不要使用单个 char
作为密钥,而应使用 string
。
string msg = "This is a message.";
string k = "97k/ -X.O"; // you can choose another key
StringBuilder sb = new StringBuilder();
for (int i = 0; i < msg.Length; i++)
{
sb.Append((char)(msg[i] ^ k[i % k.Length]));
}
Console.WriteLine(sb.ToString());
Dim msg As String = "This is a message"
Dim k as String = "97k/ -X.O"
Dim sb As New System.Text.StringBuilder()
For i As Integer = 0 To msg.Length - 1
sb.Append(ChrW(AscW(msg(i)) Xor AscW(k(i Mod k.Length))))
Next
Console.WriteLine(sb.ToString())
输出为 m_☻\ D+♫.↓Z♫\SL?Ka
。现在,您无法通过频率分析来破解它。但是,您的应用程序可以被反编译,如果有人知道密钥,则可以轻松解密您的消息。因此,不要仅使用 XOR 加密来加密消息。无论如何,如果您对安全和加密感兴趣,可以将 XOR 加密作为加密算法的一部分。
NOT 运算符
按位 NOT 运算符会反转位序列中的每一位。0
变为 1
,1
变为 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 中的 NOT 运算符
byte b = 52;
byte notB = (byte)~b; // returns 203
Int16 int16NotB = (Int16)~b; // returns -53
Dim b As Byte = 52
Dim notB As Byte = Not b ' returns 203
Dim int16NotB As Int16 = CShort(Not b) ' returns 203
在 C# 中,如果您将 ~b
转换为 short (Int16),您将得到 -53。但在 VB.NET 中,如果您将 Not b
转换为 Int16
,您将得到 203。为什么?我在 C# 中尝试了 (~b).GetType()
,得到 System.Int32
。我在 VB.NET 中尝试了 (Not b).GetType()
,得到 System.Byte
。因此,反转字节的标准数据类型在 C# 中是 Int32,在 VB.NET 中是 Byte。在此表中,我计算 (Int16)~52
:
A | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 0 | 1 | 0 | 0 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
~A (Not A) | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 1 | 0 | 1 | 1 |
将 1111111111001011 转换为十进制数(Int16 = 有符号数据类型,第一位为 1,因此为负数),结果为 -53。因此,(Int16)~52
等于 **-53**。
左移运算符
左移运算符的工作原理
x << n(左移)将 x 中的所有位向左移动 n 位,并将空出的位位置用零填充。
正如您在图像中看到的,所有位都向左移动了一个位置,空出的位位置用零填充。因此,154 << 1
等于 52
。
5 << 2
将 5 的二进制形式 (00000101) 中的所有位向左移动两位:00010100(2010 的二进制形式)。因此,5 << 2
等于 **20**。计算 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 的左移运算符
byte b1 = 154;
byte b2 = (byte)(b1 << 1);
Console.WriteLine(b2); // output: 52
Dim b1 As Byte = 154
Dim b2 As Byte = b1 << 1
Console.WriteLine(b2) ' output: 52
使用左移运算符计算 2 的幂
1 << n
返回 2n
,但使用左移计算 2 的幂比使用 Math.Pow
方法更快。
System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
sw.Start();
byte pow = 1 << 7;
sw.Stop();
Console.WriteLine(sw.Elapsed.TotalMilliseconds); // output: 0.0012 (may differ)
sw.Reset();
sw.Start();
byte mathPow = (byte)Math.Pow(2, 7);
sw.Stop();
Console.WriteLine(sw.Elapsed.TotalMilliseconds); // output: 0.0057 (may differ)
Dim sw As New System.Diagnostics.Stopwatch()
sw.Start()
Dim pow As Byte = 1 << 7
sw.[Stop]()
Console.WriteLine(sw.Elapsed.TotalMilliseconds)
' output: 0.0012 (can differ)
sw.Reset()
sw.Start()
Dim mathPow As Byte = CByte(Math.Pow(2, 7))
sw.[Stop]()
Console.WriteLine(sw.Elapsed.TotalMilliseconds)
' output: 0.0077 (can differ)
右移运算符
右移运算符的工作原理
x >> n(右移)将 x 中的所有位向右移动 n 位,并将空出的位位置用零填充。
正如您在图像中看到的,所有位都向右移动了一个位置,空出的位位置用零填充。 因此,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 的右移运算符
byte b1 = 155;
byte b2 = (byte)(b1 >> 1);
Console.WriteLine(b2); // output: 77
Dim b1 As Byte = 155
Dim b2 As Byte = b1 >> 1
Console.WriteLine(b2) ' output: 77
使用右移运算符计算 x / 2n
x >> n
等于 x / 2n
。例如,8 >> 2
等于 8 / 22
,即 8 / 4
,等于 2
。因此,8 >> 2
等于 2
。
byte b = (byte)(8 >> 2);
Console.WriteLine(b); // output: 2
Dim b As Byte = 8 >> 2
Console.WriteLine(b) ' output: 2
这也比 8 / Math.Pow(2, 2);
快。
System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
sw.Start();
byte divisionUsingRightShift = 8 >> 2;
sw.Stop();
Console.WriteLine(sw.Elapsed.TotalMilliseconds); // output: 0.0016 (may differ)
sw.Reset();
sw.Start();
byte divisionUsingMathPow = (byte)(8 / Math.Pow(2, 2));
sw.Stop();
Console.WriteLine(sw.Elapsed.TotalMilliseconds); // output: 0.0073 (may differ)
Dim sw As New System.Diagnostics.Stopwatch()
sw.Start()
Dim divisionUsingRightShift As Byte = 8 >> 2
sw.[Stop]()
Console.WriteLine(sw.Elapsed.TotalMilliseconds) ' output: 0.0016 (may differ)
sw.Reset()
sw.Start()
Dim divisionUsingMathPow As Byte = CByte(8 / Math.Pow(2, 2))
sw.[Stop]()
Console.WriteLine(sw.Elapsed.TotalMilliseconds) ' output: 0.0102 (may differ)
循环移位
循环左移
循环左移将 b
中的所有位向左移动 n 位,并将最后一个位填充为移位前字节的第一个位。
图像显示 154 circularleftshift 1
。(对于 byte
)154 circularleftshift 1
等于 154 << 1 | 154 >> 7
,而 a circularleftshift n
等于 a << n | a >> (b - n)
。b
是位数。因此,对于 byte
,公式为 a << n | a >> (8 - n)
,对于 Int32
,公式为 a << n | a >> (32 - n)
。C# 和 VB.NET 中的循环左移(对于 byte
)
static byte CircularLeftShift(byte a, byte n)
{
return (byte)(a << n | a >> (8 - n));
}
// Usage:
byte b1 = CircularLeftShift(154, 1); // calculates 154 circularleftshift 1
// value of b1: 53
byte b2 = CircularLeftShift(154, 2); // calculates 154 circularleftshift 2
// value of b2: 106
Private Function CircularLeftShift(a As Byte, n As Byte) As Byte
Return CByte(a << n Or a >> (8 - n))
End Function
' Usage:
Dim b1 As Byte = CircularLeftShift(154, 1) ' calculates 154 circularleftshift 1
' value of b1: 53
Dim b2 As Byte = CircularLeftShift(154, 2) ' calculates 154 circularleftshift 2
' value of b2: 106
循环右移
循环右移将 b
中的所有位向右移动 n 位,并将第一个位填充为移位前字节的最后一个位。
图像显示 155 circularrightshift 1
。(对于 byte
)155 circularrightshift 1
等于 155 >> 1 | 154 << 7
,而 a circularrightshift n
等于 a >> n | a << (b - n)
。b
是位数。因此,对于 byte
,公式为 a >> n | a << (8 - n)
,对于 Int32
,公式为 a >> n | a << (32 - n)
。C# 和 VB.NET 中的循环右移(对于 byte
)
static byte CircularRightShift(byte a, byte n)
{
return (byte)(a >> n | a << (8 - n));
}
// Usage:
byte b1 = CircularRightShift(155, 1); // calculates 155 circularrightshift 1
// value of b1: 205
byte b2 = CircularRightShift(155, 2); // calculates 155 circularrightshift 2
// value of b2: 230
Private Function CircularRightShift(a As Byte, n As Byte) As Byte
Return CByte(a >> n Or a << (8 - n))
End Function
' Usage:
Dim b1 As Byte = CircularRightShift(155, 1) ' calculates 155 circularrightshift 1
' value of b1: 205
Dim b2 As Byte = CircularRightShift(155, 2) ' calculates 155 circularrightshift 2
' value of b2: 230
历史
- 2013 年 3 月 30 日
- 已添加下载
- 2013 年 3 月 27 日
- 将 **使用右移运算符计算 x / 22** 更改为 **使用右移运算符计算 x / 2n**
- 2013 年 3 月 18 日
- 添加了 **循环移位**
- 2013 年 3 月 12 日
- 修复了 小错误[^],添加了目录
- 2013 年 3 月 9 日
- 我更改了左移和右移的描述(Andreas Gieriet 在此消息[^] 中纠正了我)。
- 2013 年 3 月 8 日
- 第一版