0xaa和0x55是做什么用的?

10

我试过谷歌搜索,但是没有找到任何能够理解的内容@.@... 有人可以用通俗易懂的语言解释一下这段代码正在发生什么吗?

这是《Cracking the Coding Interview》这本书中的一个问题。

"编写程序尽可能少地使用指令来交换整数中的奇偶位(例如,交换位0和位1,交换位2和3等)。"

我所采用的方法并不涉及位操作,因为我无法弄清楚如何%\ ...

def swap(n):

    b = bin(n)[2:]
    print(b)
    if len(b)%2 != 0:
        c = True
        b = b[0] + b

    pairs = wrap(b, 2)
    pairs = [i[::-1] for i in pairs]
    ans = ''.join(pairs)

    if c: ans = ans[1:]
    print(ans)

但现在我看着他们的答案,我真的不太明白...(不是用Python也没什么帮助):

int swapOddEvenBits(int x) {
  return ( ((x & 0xaaaaaaaa) >>> 1) | ((x & 0x55555555) << 1) );

1
它们是掩码,用于掩盖奇数位或偶数位。然后它们被同时移位,奇数位向右移动一位,偶数位向左移动一位,以进行交换。 - AntonH
2
第一部分 - 将您的int101010...进行&运算 - 这意味着奇数位保持不变,偶数位为0;对整个位向上移动一位,忽略符号。第二部分 - 将您的int010101...进行&运算 - 这意味着偶数位保持不变,奇数位为零;对整个位向下移动一位,考虑符号。现在您已经将所有正确的部分放在了正确的位置 - 将它们|在一起。将代码分解并在每个步骤中打印出二进制将有助于您。 - Boris the Spider
所以问题并不是关于 [tag:python] 或者 [tag:java] ... - handle
1
你可以使用Python来查看这些值的二进制等效形式:bin(0xaaaaaaaa) 将返回 '0b10101010101010101010101010101010',而 bin(0x55555555) 则返回 '0b1010101010101010101010101010101' - tdelaney
1
可以尝试一下Oracle的教程 - Boris the Spider
显示剩余4条评论
3个回答

13

让我们来分解一下。

return ( ((x & 0xaaaaaaaa) >>> 1) | ((x & 0x55555555) << 1) );
首先,我们来看一下(x & 0xaaaaaaaa)。如果你将0xaaaaaaaa分解为二进制位,你会得到 1010 1010 1010 1010 1010 1010 1010 1010 (由于a在二进制下是1010,所以这里用a表示)。所以(x & 0xaaaaaaaa)的意思是,只返回x中每个偶数位置上的1。这被称为位掩码。然后,你向右移动一个位置 - 这就是如何使偶数换位(现在第二位占据了第一位,第四位占据了第三位,依此类推)。
你可以用同样的方式处理(x & 0x55555555) - 如果你将其分解为二进制位,你会得到0101 0101 0101 0101 0101 0101 0101 0101 (由于5在二进制下是0101,所以这里用5表示)。它用来屏蔽x中所有的偶数位,给出所有的奇数位。然后,你将所有的位向左移动1位。最后,使用or (|)运算符来组合这两个位序列,这就是答案。
例如: 我们将2456086205转换为二进制,得到1001 0010 0110 0100 1110 0110 1011 1101。现在,我们执行(x & 0xaaaaaaaa),得到: 1001 0010 0110 0100 1110 0110 1011 1101 & 1010 1010 1010 1010 1010 1010 1010 1010
结果是1000 0010 0010 0000 1010 0010 1010 1000。将其向右移动一位,你会得到 0100 0001 0001 0000 0101 0001 0101 0100
现在,执行(x & 0x55555555),得到: 1001 0010 0110 0100 1110 0110 1011 1101 & 0101 0101 0101 0101 0101 0101 0101 0101
结果是0001 0000 0100 0100 0100 0100 0001 0101。将其左移一位,你会得到0010 0000 1000 1000 1000 1000 0010 1010
最后,执行0100 0001 0001 0000 0101 0001 0101 0100 | 0010 0000 1000 1000 1000 1000 0010 1010。你将得到0110 0001 1001 1000 1101 1001 0111 1110,就像你所看到的,这就是答案!

3
01100001是代表字母"a"的字符代码,转换成二进制后为1010。而0xa在十六进制中表示10,等于2+0+8+0,因此在二进制中表示为1010。 - Aneesh Ashutosh
哦 %\ ... 真糟糕,现在还有十六进制 X.x ... 以前从未使用过。感谢澄清。 - Raksha
1
@Raksha 起初可能有点混乱,但这绝对是值得了解的好东西。我们的数字系统=十进制,二进制=二进制,十六进制=基数16!这里有一个有用的教程:https://betterexplained.com/articles/numbers-and-bases/ - Aneesh Ashutosh
3
@Raksha,关键在于它们在内部都是相同类型的!0xaaaaaaaa是以16进制表示的数字;2456086205是以10进制表示的数字;但在计算机中,所有数字都以二进制形式表示(因为这是计算机存储数据的方式)。因此,实际上,使用不同的方法(以10进制和16进制)来告诉计算机存储一个(以2进制为基础的)数字。 - Aneesh Ashutosh
Python 中没有逻辑右移吗?这有点破坏了整个代码 :I ... 必须手动添加那个 0。 - Raksha
显示剩余2条评论

7

转换为二进制,

0xaaaaaaaa == 0b10101010101010101010101010101010
0x55555555 == 0b01010101010101010101010101010101

这些数字的0和1交替设置,因此当您使用&与其中之一的数字时,它会选择每隔一个位。
如果您使用整数执行swapOddEvenBits过程,比如说0b01111100111101001111110000110010,我们会得到:
0xaaaaaaaa & 0b01111100111101001111110000110010 selects the following bits:
               0 1 1 0 1 1 0 0 1 1 1 0 0 1 0 1     # unselected bits are 0

0x55555555 & 0b01111100111101001111110000110010 selects the following bits:
                1 1 1 0 1 1 1 0 1 1 1 0 0 1 0 0

0 1 1 0 1 1 0 0 1 1 1 0 0 1 0 1 gets shifted right:
 0 1 1 0 1 1 0 0 1 1 1 0 0 1 0 1

and
 1 1 1 0 1 1 1 0 1 1 1 0 0 1 0 0 gets shifted left:
1 1 1 0 1 1 1 0 1 1 1 0 0 1 0 0

and we | the results back together:
 0 1 1 0 1 1 0 0 1 1 1 0 0 1 0 1
1 1 1 0 1 1 1 0 1 1 1 0 0 1 0 0
-------------------------------
10111100111110001111110000110001

1
我们知道0xaa和0x55是十六进制表示法。此外,十六进制中的每个字符都使用4位表示。
因此,0xaa相当于1010 1010(因为a = 1010二进制) 而0x55相当于0101 0101(因为5 = 0101二进制)
与它们中的任何数字进行“与”运算会返回数字的交替位,因此在解决交换奇数和偶数位数字等问题时非常有用。

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