【问题标题】:How to clear the most significant bit?如何清除最高位?
【发布时间】:2011-12-09 01:36:36
【问题描述】:

如何将 int 中的最高有效位从 1 更改为 0?比如我想把 01101 改成 0101。

【问题讨论】:

  • “0 上的第一个 1”是什么意思?
  • 执行逻辑和运算,如 01101 & 01101 = 01101
  • 我相信问题是:“逐位遍历整数。第一次遇到1,将其更改为零。其他一切保持不变。”
  • 你的“位容器”是什么类型的? int, long, string... ?

标签: c# bit-manipulation


【解决方案1】:

进行这种按位运算的最简单方法是使用左移运算符 (

(1 << 3) = 100b(1 << 5) = 10000b

然后你使用一个你想要改变某个位的值并使用
| (OR) 如果要将其更改为 1 或
& ~ (AND NOT) 如果你想把它改成 0。

像这样:

int a = 13; //01101
int result = a | (1 << 5); //gives 11101
int result2 = result & ~(1 << 5); //gives 01101

【讨论】:

  • (1 &lt;&lt; 3) = 8, (1 &lt;&lt; 5) = 32
  • @LB它们是二进制值
  • 我写评论的时候他们不在
  • 这个答案不能解决 OP 要求的问题,因为 a = 13 只是一个例子(OP 说“例如”)。很明显,OP 想要的是一个适用于任意整数的解决方案。
【解决方案2】:

编辑:简化(和解释)答案

如果您的唯一目标是将最高有效位设置为零,那么我在下面给出的答案是矫枉过正。

最后一段代码构造了一个位掩码,包括数字中的所有位。

mask |= mask >> 1;
mask |= mask >> 2;
mask |= mask >> 4;
mask |= mask >> 8;
mask |= mask >> 16;

这是它对给定的 32 位无符号整数执行的一系列计算:

mask = originalValue
mask:               01000000000000000000000000000000
mask |= mask >> 1:  01100000000000000000000000000000
mask |= mask >> 2:  01111000000000000000000000000000
mask |= mask >> 4:  01111111100000000000000000000000
mask |= mask >> 8:  01111111111111111000000000000000
mask |= mask >> 16: 01111111111111111111111111111111

由于它进行了右移,不会回绕,因此它永远不会将高于最高有效位的位设置为 1。由于它使用的是逻辑 or,因此您永远不会将任何尚未为零的值显式设置为零。

从逻辑上讲,这将始终创建一个填充整个uint 的位掩码,直到并包括最初设置的最高有效位,但不会更高。

从那个掩码中,很容易将它缩小以包含所有最初设置的最高有效位:

mask = mask >> 1: 00111111111111111111111111111111

然后只需对原始值执行逻辑and,它会将数字中的所有最高有效位设置为零,直到并包括原始值的最高有效位:

originalValue &= mask: 00000000000000000000000000000000

我在这里使用的原始数字很好地显示了掩码构建过程,但它并没有很好地显示最后的计算。让我们用一些更有趣的示例值(问题中的值)来进行计算:

originalValue: 1101
mask = originalValue
mask:               00000000000000000000000000001101
mask |= mask >> 1:  00000000000000000000000000001111
mask |= mask >> 2:  00000000000000000000000000001111
mask |= mask >> 4:  00000000000000000000000000001111
mask |= mask >> 8:  00000000000000000000000000001111
mask |= mask >> 16: 00000000000000000000000000001111
mask = mask >> 1:   00000000000000000000000000000111

这是您正在寻找的价值:

originalValue &= mask: 00000000000000000000000000000101

既然我们可以看到这是可行的,让我们把最终的代码放在一起:

uint SetHighestBitToZero(uint originalValue)
{
    uint mask = originalValue;

    mask |= mask >> 1;
    mask |= mask >> 2;
    mask |= mask >> 4;
    mask |= mask >> 8;
    mask |= mask >> 16;

    mask = mask >> 1;

    return originalValue & mask;
}

// ...
Console.WriteLine(SetHighestBitToZero(13)); // 1101

5

(即0101)

我给出的原始答案

对于这类问题,我经常参考这篇文章:

"Bit Twiddling Hacks"

您想要的特定部分称为“查找整数的整数对数基数 2(也就是最高位集的位置)”。

这是一系列解决方案中的第一个(每一个都比前一个更优化):

http://graphics.stanford.edu/~seander/bithacks.html#IntegerLogObvious

文章中的最终解决方案是(转换为C#):

uint originalValue = 13;
uint v = originalValue; // find the log base 2 of 32-bit v
int r;  // result goes here

uint[] MultiplyDeBruijnBitPosition = 
{
  0, 9, 1, 10, 13, 21, 2, 29, 11, 14, 16, 18, 22, 25, 3, 30,
  8, 12, 20, 28, 15, 17, 24, 7, 19, 27, 23, 6, 26, 5, 4, 31
};

v |= v >> 1; // first round down to one less than a power of 2 
v |= v >> 2;
v |= v >> 4;
v |= v >> 8;
v |= v >> 16;

r = (int)MultiplyDeBruijnBitPosition[(uint)(v * 0x07C4ACDDU) >> 27];

找到最高设置位后,只需将其屏蔽掉:

originalValue &= ~(uint)(1 << r); // Force bit "r" to be zero

【讨论】:

  • 这本书看起来很像《Hackers Delight》一书
  • @luketorjussen:感谢插件!我去看看那本书:)
  • 有人检查过这个功能吗?
  • @L.B:我没有,但据说那个网站有。如果您不喜欢答案中的一个,那么该站点上还有 3 或 4 个变体。经过一番谷歌搜索后,我找到了这个答案,看起来它链接到描述算法的文章:stackoverflow.com/questions/757059/…
  • @Merlyn Morgan-Graham,我不想争论算法。我想说的是,您的代码没有执行问题中的要求。请检查您的代码,然后查看我的简化答案
【解决方案3】:

如果您将其视为整数,则结果将始终显示代码而没有零。可能是字符串操作会输出您想要的内容,但不确定这将如何帮助您。使用如下所示的字符串:

  string y = "01101";
  int pos = y.IndexOf("1");
  y = y.Insert(pos, "0");
  y = y.Remove(pos + 1, 1);

【讨论】:

  • 该问题已明确标记为 bit-manipulation,因此很明显 OP 想要的是速度。尽管您的答案的结果是正确的,但通过将int 转换为string 这样做会破坏问题的原始目的。
【解决方案4】:
int x = 0xD; //01101
int wk = x;
int mask = 1;
while(0!=(wk >>= 1)) mask <<= 1;
x ^= mask; //00101

【讨论】:

    【解决方案5】:

    灵感来自 Merlyn Morgan-Graham 的回答

    static uint fxn(uint v)
    {
        uint i = v;
    
        v |= v >> 1; 
        v |= v >> 2;
        v |= v >> 4;
        v |= v >> 8;
        v |= v >> 16;
    
        return (v >> 1) & i;
    }
    

    【讨论】:

      【解决方案6】:

      您可以使用以下内容(未经测试):

      int a = 13; //01101
      int value = 0x80000000;
      while ((a & value) != value)
      {
        value = value >> 1;
      }
      if (value > 0)
        a = a ^ value;
      

      【讨论】:

        【解决方案7】:

        如果您知道类型的大小,则可以移出位。 您也可以轻松地将其放入 BitArray 并翻转 MSB。

        示例 1:

        short value = -5334;
        var ary = new BitArray(new[] { (value & 0xFF00) >> 8 });
        ary.Set(7, false);
        

        示例 2:

        short value = -5334;
        var newValue = Convert.ToInt16((value << 1) >> 1);
        
        // Or
        
        newValue = Convert.ToUInt16(value);
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-02-18
          • 2021-05-30
          • 2014-05-20
          • 2014-02-13
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多