注:本文为 “Bitwise Operations” 相关文章及讨论合辑。
英文引文,机翻未校。
如有内容异常,请看原文。
Understanding Bitwise Operations and Their Uses in Programming
了解按位运算及其在编程中的用途
In the world of programming, bitwise operations are powerful tools that operate directly on the binary representations of numbers. While they may seem esoteric at first, mastering bitwise operations can lead to more efficient code and unlock solutions to complex problems. This article will delve into the intricacies of bitwise operations, their various uses, and how they can be applied in real-world programming scenarios.
在编程领域,按位运算是直接对数字的二进制表示进行作的强大工具。虽然它们乍一看可能很深奥,但掌握按位运算可以带来更高效的代码并解锁复杂问题的解决方案。本文将深入探讨 bitwise 运算的复杂性、它们的各种用途,以及如何将它们应用于实际编程场景。
What Are Bitwise Operations?
什么是按位运算?
Bitwise operations are fundamental operations that work at the bit level of numbers. Instead of treating numbers as whole entities, these operations manipulate individual bits within the binary representation of those numbers. The most common bitwise operators are:
按位运算是在数字的位级工作的基本运算。这些作不是将数字视为整个实体,而是作这些数字的二进制表示形式中的单个位。最常见的按位运算符是:
- AND (&) 和 (&)
- OR (|) 或 (|)
- XOR (^) 异或 (^)
- NOT (~) 非 (~)
- Left Shift (<<) 左移 (<<)
- Right Shift (>>) 右移 (>>)
Let’s explore each of these operators in detail.
让我们详细探讨一下这些运算符中的每一个。
1. Bitwise AND (&)
- 按位 AND (&)
The bitwise AND operator compares each bit of two numbers and returns 1 if both bits are 1, otherwise it returns 0. This operation is often used for masking or checking if a particular bit is set.
按位 AND 运算符比较两个数字的每个位,如果两个位都为 1,则返回 1,否则返回 0。此作通常用于屏蔽或检查是否设置了特定位。
1010 (10 in decimal)
& 1100 (12 in decimal)
----
1000 (8 in decimal)
2. Bitwise OR (|)
- 按位 OR (|)
The bitwise OR operator compares each bit of two numbers and returns 1 if at least one of the bits is 1, otherwise it returns 0. This operation is commonly used for combining flags or setting specific bits.
按位 OR 运算符比较两个数字的每个位,如果至少有一个位为 1,则返回 1,否则返回 0。此作通常用于组合标志或设置特定位。
1010 (10 in decimal)
| 1100 (12 in decimal)
----
1110 (14 in decimal)
3. Bitwise XOR (^)
- 按位 XOR (^)
The bitwise XOR (exclusive OR) operator compares each bit of two numbers and returns 1 if the bits are different, otherwise it returns 0. XOR has interesting properties that make it useful for various operations, including simple encryption and toggling bits.
按位 XOR(异或)运算符比较两个数字的每个位,如果位不同,则返回 1,否则返回 0。XOR 具有有趣的属性,使其可用于各种作,包括简单的加密和切换位。
1010 (10 in decimal)
^ 1100 (12 in decimal)
----
0110 (6 in decimal)
4. Bitwise NOT (~)
- 按位 NOT (~)
The bitwise NOT operator inverts all the bits of a number, changing 0s to 1s and vice versa. It’s important to note that the result depends on the number of bits used to represent the number in your system.
按位 NOT 运算符反转数字的所有位,将 0 更改为 1,反之亦然。请务必注意,结果取决于系统中用于表示数字的位数。
~ 1010 (10 in decimal)
----
0101 (5 in decimal, assuming 4-bit representation)
5. Left Shift (<<)
- 左移 (<<)
The left shift operator moves all bits in a number to the left by a specified number of positions. This operation effectively multiplies the number by 2 raised to the power of the shift amount.
左移运算符将数字中的所有位向左移动指定数量的位置。此作有效地将数字乘以 2 的 次幂 shift 量。
1010 << 1
----
10100 (20 in decimal)
6. Right Shift (>>)
- 右移 (>>)
The right shift operator moves all bits in a number to the right by a specified number of positions. This operation effectively divides the number by 2 raised to the power of the shift amount (for positive numbers).
右移运算符将数字中的所有位向右移动指定数量的位置。此作有效地将数字除以 2 的 Shift 量的幂(对于正数)。
1010 >> 1
----
0101 (5 in decimal)
Practical Applications of Bitwise Operations
按位运算的实际应用
Now that we’ve covered the basics of bitwise operations, let’s explore some practical applications where these operations shine.
现在我们已经介绍了按位运算的基础知识,让我们探索这些运算大放异彩的一些实际应用。
1. Efficient Multiplication and Division
1.高效的乘法和除法
Left and right shift operations can be used for quick multiplication and division by powers of 2. This is often faster than traditional multiplication and division operations.
左移和右移作可用于快速乘以和除以 2 的幂。这通常比传统的乘法和除法运算更快。
// Multiply by 2
int multiplyByTwo(int n) {
return n << 1;
}
// Divide by 2
int divideByTwo(int n) {
return n >> 1;
}
2. Checking Even/Odd Numbers
2.检查偶数/奇数
The bitwise AND operator can be used to efficiently check if a number is even or odd.
按位 AND 运算符可用于有效地检查数字是偶数还是奇数。
boolean isEven(int n) {
return (n & 1) == 0;
}
3. Swapping Variables Without a Temporary Variable
3.在没有临时变量的情况下交换变量
XOR can be used to swap two variables without using a temporary variable:
XOR 可用于交换两个变量,而无需使用临时变量:
void swapWithoutTemp(int &a, int &b) {
a = a ^ b;
b = a ^ b;
a = a ^ b;
}
4. Setting, Clearing, and Toggling Bits
4.设置、清除和切换位
Bitwise operations are perfect for manipulating individual bits:
按位运算非常适合处理单个位:
// Set the nth bit
int setBit(int num, int n) {
return num | (1 << n);
}
// Clear the nth bit
int clearBit(int num, int n) {
return num & ~(1 << n);
}
// Toggle the nth bit
int toggleBit(int num, int n) {
return num ^ (1 << n);
}
5. Bit Masking
5.位掩码
Bit masking is a technique used to extract or manipulate specific bits within a number. It’s commonly used in graphics programming, network protocols, and embedded systems.
位掩码是一种用于提取或作数字中特定位的技术。它通常用于图形编程、网络协议和嵌入式系统。
// Extract the last 4 bits of a number
int extractLastFourBits(int num) {
return num & 0xF; // 0xF is 1111 in binary
}
6. Efficient Power of 2 Checking
6.2 次检查的高效功能
Bitwise operations can be used to efficiently check if a number is a power of 2:
按位运算可用于有效地检查数字是否为 2 的幂:
boolean isPowerOfTwo(int n) {
return n > 0 && (n & (n - 1)) == 0;
}
Advanced Bitwise Techniques
高级 Bitwise 技术
As we delve deeper into bitwise operations, we encounter more advanced techniques that can significantly optimize certain algorithms and solve complex problems efficiently.
随着我们对按位运算的深入研究,我们会遇到更先进的技术,这些技术可以显著优化某些算法并有效地解决复杂问题。
1. The Brian Kernighan Algorithm
1.Brian Kernighan 算法
This algorithm is used to count the number of set bits (1s) in a binary number efficiently:
此算法用于有效地计算二进制数中的设置位 (1) 的数量:
int countSetBits(int n) {
int count = 0;
while (n) {
n &= (n - 1);
count++;
}
return count;
}
This algorithm works by repeatedly turning off the rightmost set bit until the number becomes zero.
此算法的工作原理是反复关闭最右侧的设置位,直到数字变为零。
2. Finding the Rightmost Set Bit
2.找到最正确的 Set Bit
To find the position of the rightmost set bit in a number:
要查找数字中最右边的 set bit 的位置:
int findRightmostSetBit(int n) {
return log2(n & -n) + 1;
}
This uses the property that for any integer n, n & -n gives a number with only the rightmost set bit of n.
这使用了这样一个属性:对于任何整数 n, n & -n 都给出一个只有最右边的 n 集合位的数字。
3. XOR Properties for Solving Unique Element Problems
3.用于解决独特单元问题的 XOR 属性
XOR has some interesting properties that make it useful for solving problems involving finding unique elements:
XOR 具有一些有趣的属性,使其可用于解决涉及查找唯一元素的问题:
-
XOR of a number with itself is 0
数字与自身的 XOR 为 0 -
XOR of a number with 0 is the number itself
值为 0 的数字的 XOR 是数字本身 -
XOR is associative and commutative
XOR 是结合和交换的
These properties can be used to solve problems like finding the single non-repeating element in an array where every other element appears twice:
这些属性可用于解决诸如在数组中查找每个其他元素出现两次的单个非重复元素等问题:
int findSingleElement(int arr[], int n) {
int result = 0;
for (int i = 0; i < n; i++) {
result ^= arr[i];
}
return result;
}
4. Gray Code
4.格雷码
Gray code is a sequence of numbers where adjacent numbers differ by only one bit. It can be generated using bitwise operations:
格雷码是相邻数字仅相差一位的数字序列。它可以使用按位运算生成:
int grayCode(int n) {
return n ^ (n >> 1);
}
5. Bitwise Operations in Cryptography
5.密码学中的按位运算
Bitwise operations play a crucial role in many cryptographic algorithms. For example, the XOR operation is often used in simple encryption techniques:
按位运算在许多加密算法中起着至关重要的作用。例如,XOR作通常用于简单的加密技术中:
string xorEncrypt(string text, char key) {
string result = text;
for (int i = 0; i < text.length(); i++) {
result[i] = text[i] ^ key;
}
return result;
}
// Decryption is the same operation
string xorDecrypt(string encryptedText, char key) {
return xorEncrypt(encryptedText, key);
}
Bitwise Operations in Different Programming Languages
不同编程语言中的按位运算
While the concepts of bitwise operations remain the same across programming languages, the syntax and some specific behaviors may vary. Let’s look at how bitwise operations are implemented in some popular programming languages:
虽然按位运算的概念在编程语言中保持不变,但语法和某些特定行为可能会有所不同。让我们看看一些流行的编程语言是如何实现按位运算的:
C/C++
C and C++ provide direct support for all bitwise operators:
C 和 C++ 直接支持所有按位运算符:
#include <iostream>
using namespace std;
int main() {
int a = 5, b = 3;
cout << "AND: " << (a & b) << endl;
cout << "OR: " << (a | b) << endl;
cout << "XOR: " << (a ^ b) << endl;
cout << "NOT: " << (~a) << endl;
cout << "Left Shift: " << (a << 1) << endl;
cout << "Right Shift: " << (a >> 1) << endl;
return 0;
}
Python
Python also supports all bitwise operators, but uses different symbols for some operations:
Python 还支持所有按位运算符,但对某些作使用不同的符号:
a, b = 5, 3
print(f"AND: {a & b}")
print(f"OR: {a | b}")
print(f"XOR: {a ^ b}")
print(f"NOT: {~a}")
print(f"Left Shift: {a << 1}")
print(f"Right Shift: {a >> 1}")
Java
Java’s bitwise operators are similar to C/C++:
Java 的按位运算符类似于 C/C++:
public class BitwiseDemo {
public static void main(String[] args) {
int a = 5, b = 3;
System.out.println("AND: " + (a & b));
System.out.println("OR: " + (a | b));
System.out.println("XOR: " + (a ^ b));
System.out.println("NOT: " + (~a));
System.out.println("Left Shift: " + (a << 1));
System.out.println("Right Shift: " + (a >> 1));
}
}
JavaScript
JavaScript supports bitwise operations, but it’s important to note that all numbers in JavaScript are floating-point, so bitwise operations convert them to 32-bit integers:
JavaScript 支持按位运算,但需要注意的是,JavaScript 中的所有数字都是浮点运算,因此按位运算会将它们转换为 32 位整数:
let a = 5, b = 3;
console.log("AND:", a & b);
console.log("OR:", a | b);
console.log("XOR:", a ^ b);
console.log("NOT:", ~a);
console.log("Left Shift:", a << 1);
console.log("Right Shift:", a >> 1);
console.log("Unsigned Right Shift:", a >>> 1);
Note that JavaScript has an additional unsigned right shift operator (>>>) that always fills with zeros.
请注意,JavaScript 有一个额外的无符号右移运算符 (>>>),它总是用零填充。
Performance Considerations
性能注意事项
While bitwise operations can lead to more efficient code in many cases, it’s important to consider the context and potential trade-offs:
虽然在许多情况下,按位运算可以带来更高效的代码,但重要的是要考虑上下文和可能的权衡:
Advantages:
优势:
- Speed: Bitwise operations are typically faster than their arithmetic counterparts, especially for simple operations like multiplication or division by powers of 2.
速度:按位运算通常比算术运算更快,尤其是对于乘法或除以 2 的幂等简单运算。 - Memory efficiency: Bitwise operations can be used to pack multiple boolean flags into a single integer, saving memory.
内存效率:按位运算可用于将多个布尔标志打包成一个整数,从而节省内存。 - Low-level control: They provide fine-grained control over individual bits, which is crucial in areas like embedded systems and device drivers.
低级控制:它们提供对单个位的精细控制,这在嵌入式系统和设备驱动程序等领域至关重要。
Considerations:
考虑:
- Readability: Code using bitwise operations can be less intuitive and harder to maintain if not well-documented.
可读性:如果没有充分的文档记录,使用按位运算的代码可能不太直观且更难维护。 - Portability: The behavior of bitwise operations, especially with signed integers, can vary across different systems and languages.
可移植性:按位运算的行为(尤其是有符号整数)可能因不同的系统和语言而异。 - Optimization: Modern compilers are often capable of optimizing arithmetic operations to use bitwise operations where appropriate, so manual optimization may not always be necessary.
优化:现代编译器通常能够优化算术运算,以便在适当时使用按位运算,因此可能并不总是需要手动优化。
Common Pitfalls and Best Practices
常见陷阱和最佳实践
When working with bitwise operations, be aware of these common pitfalls and follow these best practices:
使用按位运算时,请注意这些常见陷阱并遵循以下最佳实践:
Pitfalls:
陷阱:
-
Sign extension: Be careful with right shifts on signed integers, as the behavior can be platform-dependent.
符号扩展:请小心对有符号整数的右移,因为其行为可能取决于平台。 -
Overflow: Left shifts can easily cause overflow, especially when shifting by large amounts.
溢出:左移很容易导致溢出,尤其是在大量移位时。 -
Precedence: Bitwise operators often have lower precedence than arithmetic operators. Use parentheses to ensure correct order of operations.
优先级:按位运算符的优先级通常低于算术运算符。使用括号确保作顺序正确。
Best Practices:
最佳实践:
- Use constants: Define named constants for bit masks to improve readability.
Use constants(使用常量):定义位掩码的命名常量以提高可读性。 - Comment your code: Clearly explain the purpose and logic behind complex bitwise operations.
注释您的代码:清楚地解释复杂按位运算背后的目的和逻辑。 - Test thoroughly: Bitwise operations can be tricky, so ensure comprehensive testing, especially for edge cases.
彻底测试:按位运算可能很棘手,因此请确保全面测试,尤其是对于边缘情况。 - Consider alternatives: Sometimes, using standard arithmetic or boolean operations might be clearer and just as efficient.
考虑替代方案:有时,使用标准算术或布尔运算可能更清晰且同样有效。
Conclusion
结论
Bitwise operations are powerful tools in a programmer’s arsenal. They offer efficiency and elegance in solving certain types of problems, particularly in areas like low-level system programming, optimization, and algorithm design. While they may seem daunting at first, with practice and understanding, bitwise operations can become an invaluable part of your coding toolkit.
As you continue to develop your programming skills, remember that mastering bitwise operations is just one aspect of becoming a proficient coder.
按位运算是程序员武器库中的强大工具。它们在解决某些类型的问题时提供了效率和优雅,尤其是在低级系统编程、优化和算法设计等领域。虽然它们乍一看似乎令人生畏,但通过实践和理解,按位运算可以成为您编码工具包中非常宝贵的部分。随着您继续发展编程技能,请记住,掌握按位运算只是成为熟练编码人员的一个方面。
Bitwise Operations
按位运算
Bitwise operations are a set of operations on binary strings (also known as bit strings) that will evaluate and manipulate individual bits of their inputs sequentially. These operators are implemented in most high level programming languages and CPU instruction set architectures, having a wide range of use cases and being incredibly important for manipulating data at the bit level. Here, we will cover the most notable bitwise operators, explaining how they work and their applications.
按位运算是对二进制字符串(也称为位串)的一组运算,这些运算将按顺序计算和作其输入的各个位。这些运算符在大多数高级编程语言和 CPU 指令集架构中实现,具有广泛的用例,对于在位级别处理数据非常重要。在这里,我们将介绍最著名的按位运算符,解释它们的工作原理和应用。
Bitwise AND
按位 AND
Provided two equal length bit strings, the bitwise AND
will perform the logical AND
on each bit in the bit strings. For each bit position, if both binary strings contain a 1
in that position, AND
will return a 1
. Otherwise, AND
returns 0
in that position.
提供两个相等长度的位字符串,按位 AND
将对位字符串中的每个位执行逻辑 AND
。对于每个位位置,如果两个二进制字符串在该位置都包含 1
,则 AND
将返回 1
。否则,AND
在该位置返回 0
。
Programming languages will typically implement bitwise AND
with the &
operator (distinct from &&
) which supports operations over integers. Consider the following Python snippet:
编程语言通常使用 &
运算符(不同于 &&
)实现按位 AND
,该运算符支持对整数进行运算。请考虑以下 Python 代码段:
a = 0b100 # 4 represented in binary
b = 0b110 # 6 represented in binary
print(bin(a & b))
Bitwise OR
按位 OR
Bitwise OR
will perform logical OR
on each bit, returning 1
if there is a 1
in either input for each bit position, returning 0
otherwise.
按位 OR
将在每个位上执行逻辑 OR
,如果每个位位置的任一输入中有 1
,则返回 1
,否则返回 0
。
Languages typically implement OR
with the |
operator (distinct from ||
) used on integer operations:
语言通常使用用于整数运算的 |
运算符(不同于 ||
)实现 OR
:
a = 0b100
b = 0b110
print(bin(a | b))
Bitwise XOR
按位 XOR
XOR
will perform a logical XOR
on each bit, returning 1
if one and only one input contains a 1
in a position, returning 0
otherwise.
XOR
将在每个位上执行逻辑 XOR
,如果一个且只有一个输入在某个位置包含 1
,则返回 1
,否则返回 0
。
XOR
is implemented with the ^
operator in most languages:
XOR
在大多数语言中使用 ^
运算符实现:
a = 0b100
b = 0b110
print(bin(a ^ b))
Bitwise NOT 按位 NOT
Given a single input bitstring, bitwise NOT
will invert every single bit in the bit string, flipping each 1
to a 0
and vice versa.
给定单个输入位串,按位 NOT
将反转位串中的每个位,将每个 1
翻转为 0
,反之亦然。
Programming languages typically implement NOT
with the ~
operator:
编程语言通常使用 ~
运算符实现 NOT
:
a = 0b100
print(bin(~a))
Bit shifting
位移位
Most programming languages implement two operators for performing bit shifts. Given a bit string and a number n
, the left bit shift <<
will shift a bitstring n
bits to the left. If the appending a 0
byte in the least significant (leftmost) position each shift. For programming languages where the underlying data type of the bit string has a fixed byte length, a left bit shift will discard the most significant (right most) bit in the string. The following example is a bit shift executed on an unsigned 4 bit number.
大多数编程语言都实现了两个运算符来执行移位。给定一个位串和一个数字 n
,左移位 <<
会将位串 n
位向左移位。如果在每个班次的最低有效(最左侧)位置附加一个 0
字节。对于位字符串的基础数据类型具有固定字节长度的编程语言,左移位将丢弃字符串中最高有效(最右边)的位。以下示例是对无符号 4 位数字执行的移位。
The right bit shift will move every bit in the string n
bits to the right, discarding the least significant bit in the bit string and appending a 0
in the most significant position. If the input bit string is a signed data type and the sign bit is set to 1
(meaning the input is negative), then some implementations will insert a 1
in the most significant position to leave the operand negative, but this is not universal. The following is a bit shift on an unsigned 4 bit number.
右移位会将字符串 n
位中的每个位向右移动,丢弃位字符串中最低有效位,并在最高有效位置附加 0
。如果 input bit string 是有符号数据类型并且 sign bit 设置为 1
(意味着 input 为负数),则某些实现将在最高有效位置插入 1
以使作数为负数,但这并不是通用的。以下是无符号 4 位数字的位移。
Bit shifting is implemented with the <<
(for left shift) and >>
(for right shift) operators:
移位是通过 <<
(左移) 和 >>
(右移) 运算符实现的:
a = 0b110
print(bin(a >> 2))
b = 0b1
print(bin(b << 3))
Applications of bitwise operations
按位运算的应用
Operations on bit-based data structures
对基于位的数据结构的作_.
Many data structures can be represented purely as a string of bits, meaning that they can easily be manipulated by bitwise operations. The the most notable examples of bit-level data structures are bit fields. and bitmaps., and both representations are easily modifiable and queryable via bitwise operations.
许多数据结构可以纯粹表示为一串位,这意味着它们可以很容易地通过按位运算进行作。位级数据结构最显著的示例是位字段和位图,这两种表示形式都可以通过按位作轻松修改和查询。
Optimizing multiplication with bit shifts
使用移位优化乘法
In many cases, bitwise operations can be significantly faster ways to perform certain mathematical operations instead of standard arithmetic at the CPU instruction level. Many compilers take advantage of this idea, and will have compiled binaries execute bit shifts instead of multiplication operations where doing so would optimize program runtime. In specific, we can take advantage of the idea that a left bit shift is the same as multiplying by 2 to speed up our program. Let’s consider a small C program running on an x86 instruction set:
在许多情况下,按位运算是执行某些数学运算而不是 CPU 指令级别的标准算术的更快方法。许多编译器利用了这个想法,并且会让编译后的二进制文件执行移位而不是乘法运算,这样做会优化程序运行时。具体来说,我们可以利用左移位等同于乘以 2 的想法来加快我们的程序。让我们考虑一个在 x86 指令集上运行的小型 C 程序:
int a = 2;
a = a * 16;
At first guess, we would expect this program to execute some multiplication instruction (in the case of x86, the instruction would be mul
or imul
), but if we decompile the binary of this program with objdumb -d
, we can see that our program is actually using shl
(x86’s left bit shift instruction) to do the multiplication!
乍一猜测,我们期望这个程序执行一些乘法指令(在 x86 的情况下,指令会是 mul
或 imul
),但是如果我们用 objdumb -d
反编译这个程序的二进制文件,我们可以看到我们的程序实际上是在使用 shl
(x86 的左移位指令)来做乘法!
0000000000001129 <main>:
1129: f3 0f 1e fa endbr64
112d: 55 push %rbp
112e: 48 89 e5 mov %rsp,%rbp
1131: c7 45 fc 02 00 00 00 movl $0x2,-0x4(%rbp) ; int a = 2 occurs here.
1138: c1 65 fc 04 shll $0x4,-0x4(%rbp) ; left shift executed here!
113c: b8 00 00 00 00 mov $0x0,%eax
1141: 5d pop %rbp
1142: c3 retq
1143: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)
114a: 00 00 00
114d: 0f 1f 00 nopl (%rax)
The reason why our program ultimately uses shll
instead of simply multiplying the two numbers is because x86’s bit shift instruction runs significantly faster than a multiplication instruction. The compiler leverages the fact that we can very easily multiply a
by 16 by instead bit shifting it by 4 instead, and runs the faster instruction.
我们的程序最终使用 shll
而不是简单地将两个数字相乘的原因是 x86 的移位指令比乘法指令运行得快得多。编译器利用了这样一个事实,即我们可以很容易地将 a
乘以 16,而是将其移位为 4,并运行更快的指令。
That said, using bit shifts in high-level source code generally does not provide a unique performance boost. For compiled languages, what instructions the CPU executes is determined by the compiler, and the level and form of optimization can vary wildly depending system architecture and the specific compiler that is used. In practical terms, this means that using bit shifts over multiplication is a readability concern instead of a performance concern, and programmers should not use bit shifts in place of multiplication to achieve performance.
也就是说,在高级源代码中使用移位通常不会提供独特的性能提升。对于编译语言,CPU 执行的指令由编译器决定,优化的级别和形式可能会因系统架构和所使用的特定编译器而有很大差异。实际上,这意味着在乘法上使用 bits shift 是一个可读性问题,而不是一个性能问题,程序员不应该使用 bits shift 来代替乘法来提高性能。
讨论 1:What are the advantages of using bitwise operations? [closed]
使用按位运算有什么好处?[已关闭]
Asked 14 years, 4 months ago
Modified 11 years, 3 months ago
Closed 10 years ago. 10 年前关闭。
Following reading the latest CodeProject newsletter, I came across this article on bitwise operations. It makes for interesting reading, and I can certainly see the benefit of checking if an integer is even or odd, but testing if the n-th bit is set? What can possibly be the advantages of this?
在阅读了最新的 CodeProject 时事通讯后,我看到了这篇关于按位运算的文章。它使阅读变得有趣,我当然可以看到检查整数是偶数还是奇数的好处,但测试是否设置了第 n 位呢?这样做可能有什么好处呢?
edited Nov 15, 2011 at 14:09 Thomas Owens
asked Oct 22, 2010 at 9:51 billy.bob
1. 嵌入式系统中硬件寄存器编程
Bitwise operations are absolutely essential when programming hardware registers in embedded systems. For example every processor that I have ever used has one or more registers (usually a specific memory address) that control whether an interrupt is enabled or disabled. To allow an interrupt to fire the usual process is to set the enable bit for that one interrupt type while, most importantly, not modifying any of the other bits in the register.
在嵌入式系统中对硬件寄存器进行编程时,位运算绝对至关重要。例如,我用过的每一个处理器都有一个或多个寄存器(通常是一个特定的内存地址),用于控制中断是启用还是禁用。要允许一个中断触发,通常的过程是设置该中断类型的使能位,同时最重要的是,不修改寄存器中的任何其他位。
2. 中断源快速解码
When an interrupt fires it typically sets a bit in a status register so that a single service routine can determine the precise reason for the interrupt. Testing the individual bits allows for a fast decode of the interrupt source.
当一个中断触发时,它通常会在状态寄存器中设置一位,以便单个服务程序能够确定中断的确切原因。测试各个位可以快速解码中断源。
3. 节省内存空间:
In many embedded systems the total RAM available may be 64, 128 or 256 BYTES (that is Bytes not kilobytes or megabytes) In this environment it is common to use one byte to store multiple data items, boolean flags etc. and then use bit operations to set and read these.
在许多嵌入式系统中,可用的总随机存取存储器(RAM)可能是64、128或256字节(这里是字节,不是千字节或兆字节)。在这种环境下,通常用一个字节存储多个数据项、布尔标志等,然后使用位运算来设置和读取这些内容。
4. 数据打包与解包
I have, for a number of years been working with a satellite communications system where the message payload is 10.5 bytes. To make the best use of this data packet the information must be packed into the data block without leaving any unused bits between the fields. This means making extensive use of bitwise and shift operators to take the information values and pack them into the payload being transmitted.
多年来,我一直在研究一个卫星通信系统,其消息有效载荷为10.5字节。为了充分利用这个数据包,信息必须打包到数据块中,字段之间不能有任何未使用的位。这意味着要广泛使用位运算和移位运算符,将信息值打包到正在传输的有效载荷中。
5. 速度优势:
Basically, you use them due to size and speed considerations. Bitwise operations are incredibly simple and thus usually faster than arithmetic operations. For example to get the green portion of an rgb value, the arithmetic approach is (rgb / 256) % 256
. With bitwise operations you would do something as (rgb >> 8) & 0xFF
. The latter is significantly faster and once you’re used to it, its also easier. Generally bitwise operations come into play a lot when you need to encode/decode data in a compact and fast way.
基本上,由于空间和速度的考虑,你会使用位运算。位运算非常简单,因此通常比算术运算更快。例如,要获取RGB值中的绿色部分,算术方法是(rgb / 256) % 256
。使用位运算,你可以这样做(rgb >> 8) & 0xFF
。后者明显更快,而且一旦你习惯了,也会更容易。一般来说,当你需要以紧凑快速的方式对数据进行编码/解码时,位运算就会发挥很大作用。
6. 视频和音频编解码器应用
Bitwise operations are also used frequently in video and audio codecs, for the same reason as in embedded electronics; being able to pack five flags and an eleven bit timer into half an int is very useful when you want to make a super-efficient video codec.
位运算在视频和音频编解码器中也经常使用,原因与在嵌入式电子设备中相同;当你想要制作一个超级高效的视频编解码器时,能够将五个标志和一个11位定时器打包到半个整数中非常有用。
7. MPEG 4编码应用:
In fact, MPEG 4 even uses exponential Golomb encoding for variable-bit-length fields. A value that was 17 or 19 bits wide last packet might be only three or five bits wide this packet - and you’d figure all that out with bitwise operations.
事实上,MPEG 4甚至对可变比特长度的字段使用指数哥伦布编码。上一个数据包中宽度为17或19位的值,在这个数据包中可能只有3或5位宽 —— 而你可以通过位运算来弄清楚所有这些。
8. SIMD单元编程
It is useful when programming SIMD units, especially if the CPU’s architecture intentionally left out some SIMD instructions because they could be emulated by a few others.
在位运算在对单指令多数据(SIMD)单元进行编程时很有用,特别是当CPU架构有意省略某些SIMD指令,因为这些指令可以通过其他一些指令模拟实现时。
9. 模拟缺失指令
For example, the architecture may not define any instructions for taking the negative values of a group of 16 bytes, but that can be emulated by bitwise negating and then adding 1. Likewise, subtraction can be omitted too, because it can be emulated by taking the negative of the second operand. The availability of the “alternate route” is the reason for omitting certain instructions.
例如,架构可能没有定义任何用于获取一组16字节负值的指令,但这可以通过按位取反然后加1来模拟实现。同样,减法也可以省略,因为它可以通过取第二个操作数的负值来模拟。有 “替代方法” 可用是省略某些指令的原因。
10. 模拟更宽元素运算
Likewise, the SIMD may only support parallel 8-bit addition, without implementing addition for wider elements such as 16-bit, 32-bit or 64-bit. To emulate them, one needs to extract the sign bit from the 8-bit calculation result, then perform the carry operation on the next element.
同样,SIMD可能只支持并行8位加法,而没有实现对更宽元素(如16位、32位或64位)的加法运算。要模拟这些运算,需要从8位计算结果中提取符号位,然后对下一个元素执行进位操作。
11. 其他优势总结
Packing data, quicker ops (multiplication, division, and modulus are significantly faster if aligned to powers of 2), bit flipping, etc. Learn them and start using them and you’ll slowly start to see most of the advantages on your own.
数据打包、更快的运算(如果与2的幂对齐,乘法、除法和取模运算会明显更快)、位翻转等。学习并开始使用位运算,你会逐渐自己发现它们的大部分优势。
讨论 2
Why is addition as fast as bit-wise operations in modern processors?
为什么在现代处理器中加法和按位运算一样快?
Asked 7 years, 9 months ago
Modified 6 years, 7 months ago
I know that bit-wise operations are so fast on modern processors, because they can operate on 32 or 64 bits on parallel, so bit-wise operations take only one clock cycle. However addition is a complex operation that consists of at least one and possibly up to a dozen bit-wise operations, so I naturally thought it will be 3-4 times slower. I was surprised to see after a simple benchmark that addition is exactly as fast as any of the bit-wise operations(XOR, OR, AND etc). Can anyone shed light on this?
我知道现代处理器上的按位运算非常快,因为它们可以并行处理 32 位或 64 位运算,因此按位运算只需要一个 clock cycle。然而,加法是一个复杂的作,由至少一个甚至可能多达十几个位作组成,所以我自然认为它会慢 3-4 倍。我惊讶地发现,在一个简单的基准测试之后,加法的速度与任何按位运算(XOR、OR 等)完全相同。谁能解释一下这个?
edited May 29, 2017 at 17:27
asked May 23, 2017 at 23:44
Teodor Dyakov
1. CPU设计优化加法运算
Addition is fast because CPU designers have put in the circuitry needed to make it fast. It does take significantly more gates than bitwise operations, but it is frequent enough that CPU designers have judged it to be worth it. See https://en.wikipedia.org/wiki/Adder_(electronics).
加法运算速度快是因为CPU设计者设计了专门的电路来加速它。加法运算所需的逻辑门数量明显多于位运算,但由于其使用频率很高,CPU设计者认为这样的设计是值得的。可参考https://en.wikipedia.org/wiki/Adder_(electronics)。
2. 时钟周期与执行速度的关系
Processors are clocked, so even if some instructions can clearly be done faster than others, they may well take the same number of cycles.
处理器按固定时钟周期运行,所以即使某些指令明显比其他指令执行得更快,它们也可能花费相同数量的时钟周期。
3. 微架构对运算速度测量的影响
Nowadays, processors tend to be pipelined, multi-scalar, with out-of-order execution and whatever else. That means that they are able to execute several instructions at the same time, at various stage of completion. If you want to show by measurements that an operation takes more time that another, you have to take take those aspect in consideration as their goal is to hide those difference. You may very well have the same throughput for addition and bitwise operations when using independent data but a measure of the latency or introducing dependencies between the operations may show otherwise. And you have also to be sure that the bottleneck of your measure is in the execution, and not for instance in the memory accesses.
如今,处理器通常采用流水线、超标量以及乱序执行等技术。这意味着它们能够同时在不同的完成阶段执行多条指令。如果你想通过测量表明一种运算比另一种运算花费更多时间,就必须考虑这些因素,因为这些技术的目的就是隐藏这些差异。当使用独立数据时,加法运算和位运算的吞吐量可能相同,但测量延迟或引入运算之间的依赖关系时,结果可能会有所不同。此外,你还必须确保测量的瓶颈在于运算执行本身,而不是例如内存访问。
4. 关键路径与时钟周期的关系
The duration of a cycle is determined by the longest critical path. Basically, it’s the longest amount of time necessary for some stage of the pipline to complete. If you want to make the CPU faster, you need to optimize the critical path. If reducing the critical path per se is not possible, it can be split into 2 stages of the pipeline, and you are now able to clock your CPU at almost twice the frequency (assuming there is not another critical path that prevents you from doing this). But this comes with an overhead: you need to insert a register between the stages of the pipeline. Which means that you don’t really gain 2x speed (the register needs time to store the data), and you have complicated the whole design.
时钟周期的时长由最长的关键路径决定。基本上,它是流水线中某个阶段完成所需的最长时间。如果你想让CPU运行得更快,就需要优化关键路径。如果无法直接缩短关键路径,可以将其拆分为流水线的两个阶段,这样你就能够将CPU的时钟频率提高近一倍(前提是不存在其他关键路径阻碍你这样做)。但这会带来额外开销:你需要在流水线的两个阶段之间插入一个寄存器。这意味着你实际上并没有获得两倍的速度提升(因为寄存器存储数据需要时间),而且还使整个设计变得更加复杂。
5. 高效加法运算方法及应用
There are already quite efficient methods for performing addition (e.g. carry lookahead adders) and addition is not a critical path for the processor speed, thus is makes no sense splitting it into multiple cycles.
已经有相当高效的加法运算方法(例如超前进位加法器),并且加法运算不是影响处理器速度的关键路径,因此将其拆分成多个时钟周期没有意义。
6. 指令重要性及处理器设计平衡
Add and subtract are so common operations that you want to be able to execute them in a single cycle. As a result, it doesn’t matter that AND, OR etc. could execute faster: They still need that one cycle.
加法和减法是非常常用的运算,人们希望它们能在单个时钟周期内执行。因此,即使与、或等运算可以执行得更快也没有关系,它们仍然需要一个时钟周期。
7. 加法运算的本质
Yes: integer addition is a bitwise operation (with a few more bits than the others, but still). There is no need to do anything in stages, there is no need for complicated algorithms, clocks or anything else.
没错,整数加法本身就是一种位运算(只是涉及的位数比其他位运算略多,但本质仍是位运算)。进行整数加法运算时,无需分阶段进行,也不需要复杂的算法、时钟或其他额外的东西。
8. 不同指令集架构下指令执行周期
Especially CPUs with rich instruction sets (google CISC vs. RISC) can easily take more than one cycle for even simple commands. With interleaving, even simples commands might break down into fetch-exec-store with 3 clocks (as an example).
特别是那些具有复杂指令集的CPU(可搜索“CISC与RISC”了解更多),即使是简单的指令也可能需要多个时钟周期才能执行完成。在指令交错执行的情况下,简单指令甚至可能会分解为取指、执行、存储三个步骤,每个步骤各占一个时钟周期(这只是一个示例)。
9. 加法运算在硬件实现上的特点
No, integer addition is a simple operation; subtraction as well. It is very easy to implement adders in full hardware, and they do their stuff as instantaneously as basic bit operations.
不,整数加法是一种简单的运算,减法也是。在硬件层面完全实现加法器是非常容易的,并且加法器执行运算的速度和基本位运算一样快。
位运算全面总结
HeZephyr已于 2024 - 07 - 28 14:18:32 修改
1 位运算概述
我们知道,计算机中的数在内存中都是以二进制形式进行存储的 ,而位运算就是直接对整数在内存中的二进制位进行操作,因此其执行效率非常高,在程序中尽量使用位运算进行操作,这会大大提高程序的性能。