(i<<< 48)| ((i& 0xffff0000L)<< 16)| ((i>>> 16)& 0xffff0000L)| (i>>> 48)工作?

时间:2012-03-02 06:57:06

标签: java long-integer bits

这是Long中反向的实现:

public static long reverse(long i) {
        // HD, Figure 7-1
    i = (i & 0x5555555555555555L) << 1 | (i >>> 1) & 0x5555555555555555L;//1
    i = (i & 0x3333333333333333L) << 2 | (i >>> 2) & 0x3333333333333333L;//2
    i = (i & 0x0f0f0f0f0f0f0f0fL) << 4 | (i >>> 4) & 0x0f0f0f0f0f0f0f0fL;//3
    i = (i & 0x00ff00ff00ff00ffL) << 8 | (i >>> 8) & 0x00ff00ff00ff00ffL;//4
    i = (i << 48) | ((i & 0xffff0000L) << 16) |
        ((i >>> 16) & 0xffff0000L) | (i >>> 48);//5
    return i;
}

我能理解第1,2,3,4行,但不能理解5!它是如何工作的?

我将64位分组为8组,即1是前8位,2是后8位,依此类推。

然后在第4行之后,像4,3,2,1,8,7,6,5

这样的序列

我认为第5行在|操作之前的工作方式如下:

6,5,0,0,0,0,0,0-->(i << 48)
8,7,0,0,0,0,0,0-->((i & 0xffff0000L) << 16)
0,0,0,0,4,3,2,1-->((i >>> 16) & 0xffff0000L)
0,0,0,0,0,0,2,1-->(i >>> 48)

但是,我不知道它错在哪里或是否错了!几乎整整一天都在考虑它!

有人可以帮帮我!!感谢。

哦,我犯了这样的错误:

6,5,0,0,0,0,0,0-->(i << 48)
0,0,8,7,0,0,0,0-->((i & 0xffff0000L) << 16)
0,0,0,0,2,1,0,0-->((i >>> 16) & 0xffff0000L)
0,0,0,0,0,0,4,3-->(i >>> 48)

但我认为这是错误的!我认为正确的序列是8,7,6,5,4,3,2,1

我很抱歉我犯了一些错误!它的工作原理如下:

在第4行之后

,正确的模式是:2,1,4,3,6,5,8,7

8,7,0,0,0,0,0,0-->(i << 48)
0,0,6,5,0,0,0,0-->((i & 0xffff0000L) << 16)
0,0,0,0,4,3,0,0-->((i >>> 16) & 0xffff0000L)
0,0,0,0,0,0,2,1-->(i >>> 48)

2 个答案:

答案 0 :(得分:7)

线1成对地交换相邻的单个比特(0 - 1; 2 - 3;等等)。第2-4行交换相邻的两位,4位和8位序列。此时,原始值已转换为4个16位的块,每个块与开始时的块相反。第5行然后重新排列4个块。基本上,第5行将两个步骤合二为一:交换两对16位块并交换一对32位块。逻辑是:

  • (i << 48)将最右边的16位块移动到左侧位置,将所有其他位置为零
  • ((i & 0xffff0000L) << 16)将右边的第二个块从左边移动到第二个块(所有其他位为零)
  • ((i >>> 16) & 0xffff0000L)将第二个块从左边移动到右边的第二个块(所有其他位为零)
  • (i >>> 48)将最左边的块移动到正确的位置(所有其他位为零)

然后这四个值| - 一起产生最终的反转。如果它分两步完成,那么它将是两个语句,看起来就像前四个语句一样,但具有不同的掩码模式。

我认为在第4行之后,模式为2,1,4,3,6,5,8,7,而不是4,3,2,1,8,7,6,5。然后,第5行的四个部分是:

8,7,0,0,0,0,0,0-->(i << 48)
0,0,6,5,0,0,0,0-->((i & 0xffff0000L) << 16)
0,0,0,0,4,3,0,0-->((i >>> 16) & 0xffff0000L)
0,0,0,0,0,0,2,1-->(i >>> 48)

答案 1 :(得分:1)

你的尝试不太正确。这是更正后的版本:

2,1,4,3,6,5,8,7 --> i       //  Assume this sequence after line 4
8,7,0,0,0,0,0,0 --> (i << 48)
0,0,6,5,0,0,0,0 --> ((i & 0xffff0000L) << 16)
0,0,0,0,4,3,0,0 --> ((i >>> 16) & 0xffff0000L)
0,0,0,0,0,0,2,1 --> (i >>> 48)

这是分解的两个中间步骤:

2,1,4,3,6,5,8,7 --> i       //  Assume this sequence after line 4
0,0,0,0,6,5,0,0 --> (i & 0xffff0000L)
0,0,6,5,0,0,0,0 --> ((i & 0xffff0000L) << 16)

2,1,4,3,6,5,8,7 --> i       //  Assume this sequence after line 4
0,0,2,1,4,3,6,5 --> (i >>> 16)
0,0,0,0,4,3,0,0 --> ((i >>> 16) & 0xffff0000L)

虽然我有点惊讶为什么它没有实现如下:

i = (i & 0x5555555555555555L) <<  1 | (i >>>  1) & 0x5555555555555555L;  //  1
i = (i & 0x3333333333333333L) <<  2 | (i >>>  2) & 0x3333333333333333L;  //  2
i = (i & 0x0f0f0f0f0f0f0f0fL) <<  4 | (i >>>  4) & 0x0f0f0f0f0f0f0f0fL;  //  3
i = (i & 0x00ff00ff00ff00ffL) <<  8 | (i >>>  8) & 0x00ff00ff00ff00ffL;  //  4
i = (i & 0x0000ffff0000ffffL) << 16 | (i >>> 16) & 0x0000ffff0000ffffL;  //  5
i = (i & 0x00000000ffffffffL) << 32 | (i >>> 32) & 0x00000000ffffffffL;  //  6

它使模式保持一致。 我认为它也减少了操作次数。

编辑:我知道为什么它的实现方式如此。问题中的版本仅使用 9次操作进行最后两次反转。此处的版本(第5行和第6行)需要 10次操作

Geez ......谈到微观优化到极致......


编辑2:为什么我没有想到这个?为什么java.lang.Long没有使用它?

i = (i & 0x5555555555555555L) <<  1 | (i >>>  1) & 0x5555555555555555L;  //  1
i = (i & 0x3333333333333333L) <<  2 | (i >>>  2) & 0x3333333333333333L;  //  2
i = (i & 0x0f0f0f0f0f0f0f0fL) <<  4 | (i >>>  4) & 0x0f0f0f0f0f0f0f0fL;  //  3
i = (i & 0x00ff00ff00ff00ffL) <<  8 | (i >>>  8) & 0x00ff00ff00ff00ffL;  //  4
i = (i & 0x0000ffff0000ffffL) << 16 | (i >>> 16) & 0x0000ffff0000ffffL;  //  5
i = (i << 32) | (i >>> 32)                                               //  6