繁体   English   中英

如何执行字节的循环旋转?

[英]How do I perform a circular rotation of a byte?

我正在尝试实现一个函数,该函数执行一个字节向左和向右的循环旋转。

我为这两个操作编写了相同的代码。 例如,如果您向左旋转1010变为0101 这是对的吗?

unsigned char rotl(unsigned char c) {
    int w;
    unsigned char s = c;
    for (w = 7; w >= 0; w--) {
       int b = (int)getBit(c, w);//
       if (b == 0) {
           s = clearBit(s, 7 - w);
       } else if (b == 1) {
           s = setBit(s, 7 - w);
       }
    }
    return s;
}

unsigned char getBit(unsigned char c, int n) {
    return c = (c & (1 << n)) >> n;
}

unsigned char setBit(unsigned char c, int n) {
    return c = c | (1 << n);
}

unsigned char clearBit(unsigned char c, int n) {
    return c = c &(~(1 << n));
}

C 中没有旋转运算符,但是如果你写:

unsigned char rotl(unsigned char c)
{
    return (c << 1) | (c >> 7);
}

然后,根据这个: http : //www.linux-kongress.org/2009/slides/compiler_survey_felix_von_leitner.pdf (第 56 页),编译器将找出您想要做什么并仅在其中执行旋转(非常快) ) 指令。

阅读到目前为止的答案和评论,似乎对您要完成的任务有些困惑——这可能是因为您使用的词语。 在位操作中,您可以执行多种“标准”操作。 我将总结其中的一些以帮助澄清不同的概念。 在接下来的所有内容中,我将使用abcdefgh表示 8 位(可以是 1 或 0)——并且当它们四处移动时,相同的字母将指代相同的位(可能在不同的位置); 如果有点变成“肯定是01 ,我会这样表示)。

1)位移位:这本质上是“快速乘以或除以 2 的幂”。 使用的符号是<<表示“左移”(乘)或>>表示右移(除)。 因此

abcdefgh >> 2 = 00abcdef

(相当于“除以四”)和

abcdefgh << 3 = abcdefgh000 

(相当于“乘以八” - 并假设有“空间”可以将abc移入;否则这可能会导致溢出)

2)位掩码:有时您想将某些位设置为零。 您可以通过对一个数字执行 AND 运算来实现这一点,该数字在您想要保留一点的地方和在您想要清除一点的地方为零。

abcdefgh & 01011010 = 0b0de0g0

或者,如果您想确保某些位为 1,您可以使用 OR 操作:

abcdefgh | 01011010 = a1c11f1h

3)循环移位:这有点棘手 - 在某些情况下,您希望“移动位”,而“在一端脱落”的那些又重新出现在另一端。 在 C 中没有这个符号,也没有“快速指令”(尽管大多数处理器都有一个内置指令,汇编代码可以利用它进行 FFT 计算等)。 如果你想做三个位置的“左循环移位”:

circshift(abcdefgh, 3) = defghabc

(注意:标准 C 库中没有circshift函数,尽管它存在于其他语言中 - 例如 Matlab)。 同样,“右移”将是

circshift(abcdefgh, -2) = ghabcdef

4)位反转:有时您需要反转数字中的位。 反转位时,没有“左”或“右” - 反转是反转:

reverse(abcdefgh) = hgfedcba

同样,标准 C 库中实际上没有“反向”函数。

现在,让我们来看看在 C 中实现这最后两个函数( circshiftreverse )的一些技巧。有整个网站都致力于“巧妙地操作位” - 例如,参见这个优秀的网站 对于“位黑客”的精彩集合,尽管其中一些可能有点高级......

unsigned char circshift(unsigned char x, int n) {
  return (x << n) | (x >> (8 - n));
}

这使用了上面的两个技巧:移位位,以及使用 OR 操作将位设置为特定值。 让我们看看它是如何工作的,对于 n = 3(注意 - 我忽略了第 8 位以上的位,因为函数的返回类型是unsigned char ):

(abcdefgh << 3)       = defgh000
(abcdefgh >> (8 - 3)) = 00000abc

取这两个的按位或给出

defgh000 | 00000abc = defghabc

这正是我们想要的结果。 另请注意, a << na >> (-n) 换句话说,右移负数与左移正数相同,反之亦然

现在让我们看看reverse函数。 有“快速方法”和“慢速方法”可以做到这一点。 您上面的代码给出了一种“非常慢”的方式 - 让我向您展示一种“非常快”的方式,假设您的编译器允许使用 64 位( long long )整数。

unsigned char reverse(unsigned char b) {
  return (b * 0x0202020202ULL & 0x010884422010ULL) % 1023;
}

你可能会问自己“刚刚发生了什么”??? 让我告诉你:

b = abcdefgh

* 0x0000000202020202 = 00000000 00000000 0000000a bcdefgha bcdefgha bcdefgha bcdefgha bcdefgh0
& 0x0000010884422010 = 00000000 00000000 00000001 00001000 10000100 01000010 00100000 00010000
                     = 00000000 00000000 0000000a 0000f000 b0000g00 0c0000h0 00d00000 000e0000

请注意,我们现在只拥有所有位 - 它们只是处于一种相当奇怪的模式中。 模 1023 除法将感兴趣的位“折叠”在彼此之上 - 这就像魔术一样,我无法解释。 结果确实是

hgfedcba

实现相同目的的稍微不那么晦涩的方法(效率较低,但对较大的数字非常有效)认识到,如果您交换相邻位,然后是相邻位对,然后是相邻半字节(4 位组),等等 - 你最终得到完全位反转。 在这种情况下,字节反转变为

unsigned char bytereverse(unsigned char b) {
  b = (b & 0x55) << 1 | (b & 0xAA) >> 1; // swap adjacent bits
  b = (b & 0x33) << 2 | (b & 0xCC) >> 2; // swap adjacent pairs
  b = (b & 0x0F) << 4 | (b & 0xF0) >> 4; // swap nibbles
  return b;
}

在这种情况下,字节b = abcdefgh会发生以下情况:

b & 0x55 = abcdefgh & 01010101 = 0b0d0f0h << 1 = b0d0f0h0
b & 0xAA = abcdefgh & 10101010 = a0c0e0g0 >> 1 = 0a0c0e0g
OR these two to get badcfehg

下一行:

b & 0x33 = badcfehg & 00110011 = 00dc00hg << 2 = dc00hg00
b & 0xCC = badcfehg & 11001100 = ba00fe00 >> 2 = 00ba00fe
OR these to get dcbahgfe

最后一行:

b & 0x0F = dcbahgfe & 00001111 = 0000hgfe << 4 = hgfe0000
b & 0xF0 = dcbahgfe & 11110000 = dcba0000 >> 4 = 0000dcba
OR these to get hgfedcba

这是您所追求的反转字节。 应该很容易看出多几行(类似于上面的)如何让你得到一个反转的整数(32 位)。 随着数字大小的增加,这个技巧相对来说变得越来越有效。

我相信您正在寻找的答案是上面的“某处”。 如果不出意外,我希望您对 C 中位操作的可能性有更清晰的了解。

如果根据您的评论,您想精确地移动一位,那么实现这一目标的一种简单方法是:

unsigned char rotl(unsigned char c)
{
    return((c << 1) | (c >> 7));
}

您的代码所做的是反转位; 不旋转它们。 例如,它会将 10111001 变成 10011101,而不是 01110011。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM