如何设置、清除和切换单个位

3120
如何设置、清除和切换一个位?

90
阅读此链接:http://graphics.stanford.edu/~seander/bithacks.html,当您掌握了它后,请阅读此链接:http://realtimecollisiondetection.net/blog/?p=78。 - ugasoft
22
您可能还想查看The Bit Twiddler, Bit Twiddling HacksThe Aggregate Magic Algorithms. 这些网站会对您有所帮助。 - anon
这引出了一个问题,即多个位的规范问题是什么。 - Peter Mortensen
27个回答

6

如何设置、清除和切换单个位?

为了解决在尝试形成掩码时常见的编码陷阱:
1并不总是足够宽

number是比1更宽的类型时会出现什么问题?
x可能太大,导致移位1 << x 出现未定义行为(UB) 。 即使x不太大,~可能不会翻转足够多的最高有效位。

// assume 32 bit int/unsigned
unsigned long long number = foo();

unsigned x = 40; 
number |= (1 << x);  // UB
number ^= (1 << x);  // UB
number &= ~(1 << x); // UB

x = 10;
number &= ~(1 << x); // Wrong mask, not wide enough

为确保1足够宽:

代码可以使用1ull或者严谨地使用(uintmax_t)1,让编译器进行优化。

number |= (1ull << x);
number |= ((uintmax_t)1 << x);

或者进行类型转换 - 这会导致在维护和检查代码时出现问题,需要确保类型转换正确并及时更新。

number |= (type_of_number)1 << x;

或者通过强制进行至少与number类型一样宽的数学运算,轻松地将1提升。

number |= (number*0 + 1) << x;

与大多数位运算一样,最好使用无符号类型而不是有符号类型。

有趣的看法!无论是number |= (type_of_number)1 << x;还是number |= (number*0 + 1) << x;都不适用于设置有符号类型的符号位...事实上,number |= (1ull << x);也不适用。有没有一种通过位置来实现可移植的方法呢? - chqrlie
1
在我看来,避免设置符号位并冒着移位导致的未定义行为或者实现定义行为的最好方法是使用无符号类型。高度可移植的移位有符号代码过于复杂,难以被接受。 - chux - Reinstate Monica
1
很遗憾,找到这个好答案需要滚动太多! - ad absurdum

5
这个程序是基于Jeremy的解决方案的。如果有人想快速尝试一下的话。
public class BitwiseOperations {

    public static void main(String args[]) {

        setABit(0, 4);    // Set the 4th bit, 0000 -> 1000 [8]
        clearABit(16, 5); // Clear the 5th bit, 10000 -> 00000 [0]
        toggleABit(8, 4); // Toggle the 4th bit, 1000 -> 0000 [0]
        checkABit(8, 4);  // Check the 4th bit 1000 -> true
    }

    public static void setABit(int input, int n) {
        input = input | (1 << n-1);
        System.out.println(input);
    }


    public static void clearABit(int input, int n) {
        input = input & ~(1 << n-1);
        System.out.println(input);
    }

    public static void toggleABit(int input, int n) {
        input = input ^ (1 << n-1);
        System.out.println(input);
    }

    public static void checkABit(int input, int n) {
        boolean isSet = ((input >> n-1) & 1) == 1;
        System.out.println(isSet);
    }
}

输出:

8
0
0
true

没有人叫"Jeremy"。它指的是什么答案? - Peter Mortensen

4

这是我使用的一些宏:

SET_FLAG(Status, Flag)            ((Status) |= (Flag))
CLEAR_FLAG(Status, Flag)          ((Status) &= ~(Flag))
INVALID_FLAGS(ulFlags, ulAllowed) ((ulFlags) & ~(ulAllowed))
TEST_FLAGS(t,ulMask, ulBit)       (((t)&(ulMask)) == (ulBit))
IS_FLAG_SET(t,ulMask)             TEST_FLAGS(t,ulMask,ulMask)
IS_FLAG_CLEAR(t,ulMask)           TEST_FLAGS(t,ulMask,0)

4

一个模板化版本(放在头文件中),支持更改多个位(在AVR微控制器上工作):

namespace bit {
  template <typename T1, typename T2>
  constexpr inline T1 bitmask(T2 bit) 
  {return (T1)1 << bit;}
  template <typename T1, typename T3, typename ...T2>
  constexpr inline T1 bitmask(T3 bit, T2 ...bits) 
  {return ((T1)1 << bit) | bitmask<T1>(bits...);}

  /** Set these bits (others retain their state) */
  template <typename T1, typename ...T2>
  constexpr inline void set (T1 &variable, T2 ...bits) 
  {variable |= bitmask<T1>(bits...);}
  /** Set only these bits (others will be cleared) */
  template <typename T1, typename ...T2>
  constexpr inline void setOnly (T1 &variable, T2 ...bits) 
  {variable = bitmask<T1>(bits...);}
  /** Clear these bits (others retain their state) */
  template <typename T1, typename ...T2>
  constexpr inline void clear (T1 &variable, T2 ...bits) 
  {variable &= ~bitmask<T1>(bits...);}
  /** Flip these bits (others retain their state) */
  template <typename T1, typename ...T2>
  constexpr inline void flip (T1 &variable, T2 ...bits) 
  {variable ^= bitmask<T1>(bits...);}
  /** Check if any of these bits are set */
  template <typename T1, typename ...T2>
  constexpr inline bool isAnySet(const T1 &variable, T2 ...bits) 
  {return variable & bitmask<T1>(bits...);}
  /** Check if all these bits are set */
  template <typename T1, typename ...T2>
  constexpr inline bool isSet (const T1 &variable, T2 ...bits) 
  {return ((variable & bitmask<T1>(bits...)) == bitmask<T1>(bits...));}
  /** Check if all these bits are not set */
  template <typename T1, typename ...T2>
  constexpr inline bool isNotSet (const T1 &variable, T2 ...bits) 
  {return ((variable & bitmask<T1>(bits...)) != bitmask<T1>(bits...));}
}

使用示例:

#include <iostream>
#include <bitset> // for console output of binary values

// and include the code above of course

using namespace std;

int main() {
  uint8_t v = 0b1111'1100;
  bit::set(v, 0);
  cout << bitset<8>(v) << endl;

  bit::clear(v, 0,1);
  cout << bitset<8>(v) << endl;

  bit::flip(v, 0,1);
  cout << bitset<8>(v) << endl;

  bit::clear(v, 0,1,2,3,4,5,6,7);
  cout << bitset<8>(v) << endl;

  bit::flip(v, 0,7);
  cout << bitset<8>(v) << endl;
}

顺便提一下:如果不向编译器发送优化器参数(例如:-O3),则 constexpr 和 inline 不会使用。可以随意尝试代码,并在 https://godbolt.org/ 查看 ASM 输出。


这段代码有问题。(另外,为什么你在函数定义之后加了分号“;”?) - melpomene
@melpomene 代码没有问题,我已经测试过了。你是指它无法编译还是结果不正确?至于额外的分号,我不记得了,确实可以删除。 - Joakim L. Christiansen
1
(variable & bits == bits)? - melpomene
谢谢您的注意,应该是 ((variable & bits) == bits) - Joakim L. Christiansen
3
在C++11中使用std::bitset - pqnet

3

以下是 C 语言中执行基本位运算的例程:

#define INT_BIT (unsigned int) (sizeof(unsigned int) * 8U) //number of bits in unsigned int

int main(void)
{
    
    unsigned int k = 5; //k is the bit position; here it is the 5th bit from the LSb (0th bit)
    
    unsigned int regA = 0x00007C7C; //we perform bitwise operations on regA
    
    regA |= (1U << k);    //Set kth bit
    
    regA &= ~(1U << k);   //Clear kth bit
    
    regA ^= (1U << k);    //Toggle kth bit
    
    regA = (regA << k) | regA >> (INT_BIT - k); //Rotate left by k bits
    
    regA = (regA >> k) | regA << (INT_BIT - k); //Rotate right by k bits

    return 0;   
}


-1

设置第n位为x(位值)而不使用-1

有时候当你不确定-1或类似的东西会导致什么结果时,你可能希望在不使用-1的情况下设置第n位:

number = (((number | (1 << n)) ^ (1 << n))) | (x << n);

解释: ((number | (1 << n) 将第n位设置为1(其中|表示按位或),然后使用(...) ^ (1 << n)将第n位设置为0,最后使用(...) | x << n)将原来为0的第n位设置为(位值)x

这在golang中也适用。


2
这可以更简洁地表达(而且很可能更高效,除非编译器优化了您的解决方案),如下所示:(number & ~(1 << n)) | (!!x << n) - Will Eccles

-2

在C语言中,尝试使用以下函数来更改n位:

char bitfield;

// Start at 0th position

void chang_n_bit(int n, int value)
{
    bitfield = (bitfield | (1 << n)) & (~( (1 << n) ^ (value << n) ));
}

或者

void chang_n_bit(int n, int value)
{
    bitfield = (bitfield | (1 << n)) & ((value << n) | ((~0) ^ (1 << n)));
}

或者

void chang_n_bit(int n, int value)
{
    if(value)
        bitfield |= 1 << n;
    else
        bitfield &= ~0 ^ (1 << n);
}

char get_n_bit(int n)
{
    return (bitfield & (1 << n)) ? 1 : 0;
}

1
value << n 可能导致未定义的行为。 - M.M
1更改为0x11UL以避免UB,@M.M正在谈论此事。 - user5550963

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接