我正在查看下面的位反转代码,只是想知道如何提出这些事情。 (来源:http://www.cl.cam.ac.uk/~am21/hakmemc.html)
/* reverse 8 bits (Schroeppel) */
unsigned reverse_8bits(unsigned41 a) {
return ((a * 0x000202020202) /* 5 copies in 40 bits */
& 0x010884422010) /* where bits coincide with reverse repeated base 2^10 */
/* PDP-10: 041(6 bits):020420420020(35 bits) */
% 1023; /* casting out 2^10 - 1's */
}
有人能解释一下评论“哪里的位与反向重复基数2 ^ 10”意味着什么? 另外“%1023”如何拉出相关位?这有什么一般的想法吗?
答案 0 :(得分:3)
这是一个非常广泛的问题。
以下是% 1023
可能涉及的内容的解释:您知道计算n % 9
如何将n
的基数10表示的数字相加?例如,52%9 = 7 = 5 + 2。
您的问题中的代码使用1023 = 1024 - 1而不是9 = 10 - 1执行相同的操作。它使用操作% 1023
来收集已经“独立”计算为10位切片的多个结果大量的。
这是关于如何选择常量0x000202020202
和0x010884422010
的线索的开始:它们使宽整数运算作为大数10位切片上的独立简单运算运行。
答案 1 :(得分:1)
扩展Pascal Cuoq的想法,这是一个解释。
一般的想法是,在任何基础中,如果任何数字除以( base -1),则余数将是数字中所有数字的总和
例如,34除以9时留下7作为余数。这是因为34可写为3 * 10 + 4
即。 34 = 3 * 10 + 4 = 3 *(9 +1)+ 4 = 3 * 9 +(3 +4)
现在,9除以3 * 9,留下余数(3 + 4)。这个过程可以扩展到任何基数'b',因为(b ^ n - 1)总是除以(b-1)。
现在,遇到问题,如果数字以1024为基数表示,如果数字除以1023,则余数将是其数字的总和。
要将二进制数转换为1024,我们可以将右边的10位分组为单个数字
例如,要将二进制数0x010884422010(0b10000100010000100010000100010000000010000)转换为1024,我们可以将其分组为10位数,如下所示
(1) (0000100010) (0001000100) (0010001000) (0000010000) =
(0b0000000001)*1024^4 + (0b0000100010)*1024^3 + (0b0001000100)*1024^2 + (0b0010001000)*1024^1 + (0b0000010000)*1024^0
因此,当这个数除以1023时,余数将是
的总和 0b0000000001
+ 0b0000100010
+ 0b0001000100
+ 0b0010001000
+ 0b0000010000
--------------------
0b0011111111
如果您仔细观察上述数字,则每个数字中的“1”位占据补充位置。因此,当加在一起时,它应该拉出原始数字中的所有8位。
因此,在上面的代码"a * 0x000202020202"
中,创建了5个字节“ a ”的副本。当结果与0x010884422010
进行AND运算时,我们会在“ a ”的5个副本中有选择地选择8位。当应用“% 1023
”时,我们将所有8位拉。
那么,它实际上如何反转位?那有点聪明。这个想法是,数字0b0000000001中的“1”位实际上与原始字节的MSB对齐。所以,当你“和”并且你实际上正在使用幻数位数的LSB对原始字节的MSB进行AND运算。类似于数字0b0000100010与来自MSB的第二和第六位对齐,依此类推。
因此,当您添加幻数的所有数字时,结果数字将与原始字节相反。