【问题标题】:Trouble with bitwise manipulation按位操作的问题
【发布时间】:2012-12-15 05:13:58
【问题描述】:

好的,伙计们,我知道我想做什么,但我不知道它是否已经存在(作为一个函数或理论上)或者如何表达它,所以我需要你的帮助:

  • 假设我们有一个二进制数:(msb)10101110(lsb)
  • 从位 X 开始,我想在遇到第一个零位时将所有其他位清零(向左)。
  • 尽可能快速做到这一点,所需的操作和 CPU 周期绝对最少

一个例子:

  • 编号 = 10101110,起始位置 = 1(位置 1 的位 = 1)
  • 位置++ - 位置 2 = 1,继续前进
  • 位置++ - 位置 3 = 1,继续前进
  • position++ - 位置 4 = 0,哎呀……遇到零……现在,所有内容都必须归零。

因此,我们的假想函数 CROPLEFT(X,POS)(其中 X=10101110 和 POS=1)的最终结果将返回 00001110


有什么想法吗?

【问题讨论】:

  • @MitchWheat 相信我,我已经写满了整个笔记本,里面装满了各种极端位图操作的草图。我能想到的就是这个包括一个循环(我绝对希望避免)。
  • @MitchWheat 他说这可能不是最快的方法。我同意他的看法。
  • @MitchWheat 这将被执行几百万次,所以它肯定会杀死速度,这就是原因。
  • @MitchWheat 每秒。 (以及其他十几个计算):-)
  • @MitchWheat 好的,无需保密:这是我的国际象棋引擎项目的移动生成算法的一部分。所以,如果你曾经玩过国际象棋编程和位图,你就会知道我在说什么...... ;-)

标签: c++ c 64-bit bit-manipulation


【解决方案1】:

小菜一碟。

y = ~x;    // We like working with 1's, not 0's.
y &= -y;   // Mask off all but the lowest-set bit
x &= y-1;  // Make a mask for the bits below that and apply it.

并添加了位置参数:

y = ~x & -1U<<pos; // Change 1U to a larger type if needed.
y &= -y;
x &= y-1;

关键要素是第二行,您可以通过应用逻辑和反对-y 将值y 替换为其最低设置位。遗憾的是,除非你有一个特殊的 cpu 指令,否则没有这样的运气来获得最高设置位,所以你很幸运,你的问题要求最低。

【讨论】:

  • 我认为您缺少“从位置 X 开始”部分。虽然这是一个微不足道的变化。
  • 疯了!有这么简单吗?是的,一个解释将是理想的;我会测试它,我会尽快接受。
  • @rici:是的,我错过了那个细节。可以把它作为 OP 的练习吗? :-)
  • 是的,那里的某个地方需要额外换班。 :P
  • 我继续添加它。
【解决方案2】:

好吧,到底是什么:

return x & ((x ^ (x + (1UL << POS))) | ((1UL << POS) - 1))

不管怎样,它们都是用 gcc-4.7 -O3 编译的。 R..在左边,我在右边:(两者都使用 unsigned long 和 1UL)

        .p2align 4,,15                          .p2align 4,,15
        .globl  zapleft                         .globl  zapleft2
        .type   zapleft, @function              .type   zapleft2, @function
zapleft:                                zapleft2:           
.LFB0:                                  .LFB1:
        .cfi_startproc                          .cfi_startproc
        movl    %esi, %ecx                      movl    %esi, %ecx
        movq    %rdi, %rax                      movl    $1, %edx
        movq    $-1, %rdx                       salq    %cl, %rdx
        salq    %cl, %rdx                       leaq    (%rdx,%rdi), %rax
        notq    %rax                            subq    $1, %rdx
        andq    %rax, %rdx                      xorq    %rdi, %rax
        movq    %rdx, %rax                      orq     %rdx, %rax
        negq    %rax                            andq    %rdi, %rax
        andq    %rdx, %rax                      ret
        subq    $1, %rax                        .cfi_endproc
        andq    %rdi, %rax              .LFE1:
        ret                             .size   zapleft2, .-zapleft2
        .cfi_endproc
.LFE0:
        .size   zapleft, .-zapleft

【讨论】:

  • 我在回答之前只阅读了一半的问题,有点欺骗你... ;-)
  • 对于 x=10101110 和 POS=1,这将返回 10100000(实际上相反)
  • 好的,刚刚重新测试过。我确认:它有效。猜猜我将不得不运行一些分析测试以查看哪个版本运行得更快......感谢一百万的所有努力! :-)
  • @Dr.Kameleon:我将我的版本与 R.. 的版本进行了比较,作为健全性检查,使用 pos=2 和 pos=31,用于所有 32 位整数(所以总共 2 ^34 次调用,每个函数 2^33 次)。花了 17 秒,大概在两者之间平均分配。所以这大约是每秒一亿次(内联)函数调用。这对你来说应该够快了吧?
【解决方案3】:
CROPLEFT(int X,int POS) {

    int mask = 1 << POS;

    while (X & mask)
        mask <<= 1;

    return (X & (mask - 1));
}

【讨论】:

  • 这和我自己写的差不多,但决定避免,因为那个循环。非常感谢哥们,无论如何... :-)
【解决方案4】:

用一替换尾随零:

x = x | (x-1);

用零替换尾随的:

x = x & (x+1);

编辑:哎呀,看来我误读了这个问题,上面的代码将右侧位归零,而不是左侧位!

要将左边的位归零,我们需要一个最终的 XOR 操作:

y = x | (x-1);
y = y & (y+1);
y = x ^ y;

EDIT 2关于起始位置POS

我们只需在第一步中将最右边的 POS 位清零。

y = x & (-1U<<pos);
y = y | (y-1);
y = y & (y+1);
y = x ^ y;

编辑 3 如果在 POS 处遇到第一组零,则上述解决方案将忽略它们。
如果这不能回答问题,那么代码会更短,但很像现在的 rci:

y = x | ((1U<<pos)-1); // fill trailing positions with ones
y = y & (y+1);         // replace trailing ones by zeroes
y = x ^ y;             // modify leading bits rather than trailing ones

【讨论】:

  • 因为我有昨天的检查器,所以我也检查了你的。它有效:) 它有十条指令,一条比我的长,两条比@R 的短
  • @rici 我们的代码几乎相同,我的附加步骤可能是对问题的错误解释,因为我忽略了在 POS 遇到的第一组零
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多