【问题标题】:Clear bit if another is set如果设置了另一个,则清除位
【发布时间】:2013-09-17 13:20:22
【问题描述】:

有没有类似的:
if((bitmap & BIT_WATER) && (bitmap & BIT_FIRE)) bitmap &= ~BIT_FIRE

if(bitmap & BIT_WATER) bitmap &= ~BIT_FIRE

在仅使用按位运算的单个语句中,不需要比较 (if)?

我的意思是,如果两个完全相反的标志被设置为明确的一个。

【问题讨论】:

  • bitmap &= ~(bit_fire * static_cast<bool>(bitmap & bit_water));,但它使用乘法...

标签: c++ optimization bit-manipulation


【解决方案1】:

您可以避免在第一种情况下使用bitmap & BIT_FIRE,因为如果未设置BIT_FIREbitmap &= ~BIT_FIRE; 将不会对bitmap 执行任何操作。

没有以任意方式“设置位 X,如果设置位 Y”。

当然,如果您知道BIT_FIREBIT_WATER 高一位,您可以执行bitmap &= ~(BIT_WATER << 1),这将清除“比BIT_WATER 高一位”。

【讨论】:

  • 对不起,我的错,用更正的代码编辑了问题,它应该是 ~BIT_FIRE 而不是 ~BIT_WATER。
  • @LINK2012:好的,已编辑以反映您知道的问题。简短的回答仍然是“没有通用/简单的方法来实现这一点”。
  • 如果您知道 BIT_FIRE > BIT_WATER,您也可以bitmap &= ~((bitmap & BIT_WATER) * (BIT_FIRE/BIT_WATER)) 吗?
  • @MikeDunlavey 如果BIT_FIRE/BIT_WATER == 432,例如?
  • @Petr:我想,但 OP 确实称它们为“位”:)
【解决方案2】:

可能过早的优化,但你可以这样做

bitmap &= ~((bitmap & BIT_WATER) * (BIT_FIRE/BIT_WATER)) & ~((bitmap & BIT_WATER) * (BIT_WATER/BIT_FIRE))

只要 BIT_FIRE 和 BIT_WATER 是单个位(2 的幂)。您可能还希望位图是无符号的,以确保编译器可以轻松地将其优化为一个移位、两个按位与和一个补码。

当然,一个好的编译器会将你的原始代码优化为相同的 4 条指令,没有分支。

编辑

当然,我意识到上面的说法是不正确的——只有在 BIT_FIRE > BIT_WATER 时才有效。

所以只要坚持原来的 if 并让编译器优化它...

【讨论】:

  • + 好的,你可以得到我的答案:)
【解决方案3】:

将我的评论推广到答案。请注意,它使用乘法,但也许它对您仍然有用(code on ideone.com):

#include <iostream>

int main() 
{
 int long unsigned bitmap_with_water = 0xF300003F;
 int long unsigned bitmap_without_water = 0xF300000F;
 int long unsigned bit_fire = 0x03000000;
 int long unsigned bit_water = 0x00000030;

 bitmap_with_water &= ~(bit_fire * static_cast<bool>(bitmap_with_water & bit_water));
 bitmap_without_water &= ~(bit_fire * static_cast<bool>(bitmap_without_water & bit_water));

 std::cout << (void*)(bitmap_with_water) << "\t" << (void*)(bitmap_without_water) << std::endl;

 return (0);
}

程序输出:

0xf000003f  0xf300000f

【讨论】:

    【解决方案4】:

    如果你需要写一个通用的clear_if_set(int test, int clear, int bitmap)那么这个答案是没用的。

    如果这是一个专门的函数并且你知道从火到水的移动距离:

    int water = bitmap & BIT_WATER;
    int shifted = water << WATER_TO_FIRE_LSHIFT; // for example
    bitmap &= ~shifted;
    

    单线:

    bitmap &= ~((bitmap & BIT_WATER) << WATER_TO_FIRE_LSHIFT);
    

    【讨论】:

      【解决方案5】:

      假设 BIT_WATER 和 BIT_FIRE 不是同一个位那么

      真值表

      if(bitmap & BIT_WATER) bitmap &= ~BIT_FIRE
      

      是(BIT_WATER,旧 BIT_FIRE,新 BIT_FIRE)

      0 0   0
      0 1   1
      1 0   0
      1 1   0
      

      所以从某种角度来说是

      BIT_FIRE = (~BIT_WATER)&BIT_FIRE;
      
      1 0   0
      1 1   1
      0 0   0
      0 1   0
      

      既然我不知道你的两个位之间的差距,那么这样的事情就太过分了。

      newbit = ((~(bitmap>>BIT_WATER_BIT))&(bitmap>>BIT_FIRE_BIT))&1;
      bitmap&=~BIT_FIRE;
      bitmap|=newbit<<BIT_FIRE_BIT;
      

      假设我没有打错字...如果您利用特定的位数而不是做一些通用的事情,我认为可以简化(将 BIT_WATER 位移到 BIT_FIRE 上,而不是把所有东西都右移再左移. 可能不会让它更简单。

      如果位水位为 7,位火位为 3

      bitmap = (((~(bitmap>>4))>>4)&(1<<3))&bitmap;
      

      或者这个

      bitmap = (((bitmap&(~BIT_WATER))^BIT_WATER)>>4)&bitmap;
      

      其中 >>4 是 BIT_WATER 和 BIT_FIRE 之间的方向和增量。填写适当的增量。

      【讨论】:

        【解决方案6】:

        如果使用位数而不是预先移位的位掩码是可以接受的:

        bitmap &= ~(((bitmap >> SHIFT_WATER) & 1) << SHIFT_FIRE)
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2016-11-16
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-08-12
          • 2023-02-14
          相关资源
          最近更新 更多