【问题标题】:Bitwise rotation (circular shift)按位旋转(循环移位)
【发布时间】:2021-03-03 15:14:18
【问题描述】:

我试图用 C++ 编写一些关于“按位旋转”的代码,我想通过左移来实现。我不知道如何编码,但我在“维基百科”中找到了这样的小代码。

unsigned int rotl(unsigned int value, int shift) {
    return (value << shift) | (value >> (sizeof(value) * CHAR_BIT - shift));
}

然后我试图让它工作,但是这段代码没有给出我期望的输出。前任。我有数字unsigned int 12,二进制1100,当我想用​​上面的代码按左移位按位旋转时,输出是和unsigned int 24,(11000),它必须给出输出@987654324 @,因为如果我进行按位旋转(左移),第一个 MSB 位现在必须是第一位,所有其他位都必须向左移动一位。

你能帮助理解这是什么问题吗?或者我做错了什么。

谢谢。

【问题讨论】:

  • 所以你的值是 4 位宽?您显示的代码适用于 unsigned int 类型的值,它远远超过 4 位。
  • 你的函数是正确的。整数有 32 位,而不是 4。
  • @GregHewgill 您在谈论“经典旋转移位”,但 OP 要求不同的东西。这可能是误解了“循环转变”的含义。
  • 你对 CHAR_BIT 的定义是什么,我用 8 测试它,它输出 24
  • 函数看起来没问题,实现了循环移位(通常理解)。 IE。如果将数字移 1,则位 31 将转到位置 0,而不管其值如何。您在示例中所期望和说明的内容完全不同。

标签: c++ bit-manipulation


【解决方案1】:

以下代码效果很好

#include <cstdint>

std::uint32_t rotl(std::uint32_t v, std::int32_t shift) {
    std::int32_t s =  shift>=0? shift%32 : -((-shift)%32);
    return (v<<s) | (v>>(32-s));
}

std::uint32_t rotr(std::uint32_t v, std::int32_t shift) {
    std::int32_t s =  shift>=0? shift%32 : -((-shift)%32);
    return (v>>s) | (v<<(32-s));
}

当然还有测试。

#include <iostream>

int main(){
   using namespace std;
   cout<<rotr(8,1)<<endl; // 4
   cout<<rotr(8,-1)<<endl;  //16
   cout<<rotl(8,1)<<endl;  //16
   cout<<rotl(8,-1)<<endl;  //4
   cout<<rotr(4,60)<<endl;  //64
   cout<<rotr(4,61)<<endl; //32
   cout<<rotl(4,3)<<endl;  //32
   cout<<rotl(4,4)<<endl;  //64
   return 0;
}

也许我没有提供最快的实现,但肯定是一个便携且稳定的实现

通用版

#include <cstdint>

template< class T>
inline T rotl( T v, std::int32_t shift){
    std::size_t m = sizeof(v)*std::numeric_limits<T>::digits;
    T s = shift>=0? shift%m: -((-shift)%m)
    return (v<<s) | (v>>(m-s));
}

template< class T>
inline T rotr( T v, std::int32_t shift){
    std::size_t m = sizeof(v)*std::numeric_limits<T>::digits;
    T s = shift>=0? shift%m: -((-shift)%m)
    return (v>>s) | (v<<(m-s));
}

干杯:)

【讨论】:

  • 忘记了“#include ”并且必须在通用版本的代码中删除“sizeof(v)”(使用CHAR_BITS)。任何编辑的免费积分。如果不使用汇编指令,我很难找到更快的版本
【解决方案2】:

C++20 在&lt;bit&gt; 标头中提供std::rotlstd::rotr。来自cppreference.com的例子:

#include <bit>
#include <bitset>
#include <cstdint>
#include <iostream>

int main()
{
    std::uint8_t i = 0b00011101;
    std::cout << "i          = " << std::bitset<8>(i) << '\n';
    std::cout << "rotl(i,0)  = " << std::bitset<8>(std::rotl(i,0)) << '\n';
    std::cout << "rotl(i,1)  = " << std::bitset<8>(std::rotl(i,1)) << '\n';
    std::cout << "rotl(i,4)  = " << std::bitset<8>(std::rotl(i,4)) << '\n';
    std::cout << "rotl(i,9)  = " << std::bitset<8>(std::rotl(i,9)) << '\n';
    std::cout << "rotl(i,-1) = " << std::bitset<8>(std::rotl(i,-1)) << '\n';
}

我相信std::bitset 仅在此示例中用于其流格式。

【讨论】:

    【解决方案3】:

    这是因为您使用的是 32 位的 int,因此通过将最高有效位包装到前面,它可以按预期工作。使用较小的数据结构,例如 unsigned char,使其更易于管理。

    【讨论】:

      【解决方案4】:

      整数中有超过 4 位,最有可能是 32,但理论上可能是 64 或 16。因此,值 12 的位是 00000000000000000000000000001100。左旋转 1 自然会得到值 0000000000000000000000000011000( 24).

      【讨论】:

        【解决方案5】:

        如果您想在任意位数(例如 4)上按位旋转,只需在函数中添加一个参数:

        unsigned int rotl(unsigned int value, int shift, unsigned int width) {
            return ((value << shift) & (UINT_MAX >> (sizeof(int) * CHAR_BIT - width))) | (value >> (width - shift));
        }
        

        【讨论】:

        • 您还需要将答案屏蔽为正确的位数。而且我认为您的公式也略有偏差。
        • * CHAR_BIT 在这里是错误的,假设 width 是位而不是字节。你的意思是:return ((1U &lt;&lt; width) - 1) &amp; ((value &lt;&lt; shift) | (value &gt;&gt; (width - shift)));.
        • if 测试应该是if (width &gt; sizeof(value) * CHAR_BIT),但这似乎是隐含的。无需在此处添加该分支,它会使这比需要的慢很多
        【解决方案6】:

        如果您不使用 C++20,您可能需要两个辅助函数,例如:

        template< std::size_t N >
        [[ nodiscard ]] std::bitset< N > rotl( std::bitset< N > b, std::size_t const n ) noexcept
        {
            return b << n | b >> ( N - n );
        }
        
        template< std::size_t N >
        [[ nodiscard ]] std::bitset< N > rotr( std::bitset< N > b, std::size_t const n ) noexcept
        {
            return b >> n | b << ( N - n );
        }
        

        并像使用 C++20 的 std::rotlstd::rotr 一样使用它们。

        【讨论】:

          猜你喜欢
          • 2015-12-18
          • 2015-12-23
          • 1970-01-01
          • 1970-01-01
          • 2014-02-24
          • 1970-01-01
          • 2021-03-29
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多