我正在尝试用C ++编写ROL函数,并且已经看到了许多与之相关的问题。我的知识是这样的,我真的不明白它是如何工作的。
我正在使用以下代码
#include<stdio.h>
#define INT_BITS 32
using namespace std;
int leftRotate(int n, unsigned d){ //rotate n by d bits
return (n << d)|(n >> (INT_BITS - d));
}
int main() {
int n = 0xa3519eba;
int d = 0x0f;
printf("Left rotation of %d by %d is ", n, d);
printf("%d", leftRotate(n, d));
}
我期待得到0xcf5d51a8的结果,但我得到的是签名0x2e58。
任何关于我出错的方向都会非常感激。
答案 0 :(得分:3)
当您尝试对有符号值进行位数学运算时会发生有趣的事情,因为符号位。
#include<stdio.h>
#define INT_BITS 32
int leftRotate(unsigned n, unsigned d){ //rotate n by d bits
return (n << d)|(n >> (INT_BITS - d));
}
int main() {
unsigned n = 0xa3519eba;
unsigned d;
for( d = 0; d < 32; d += 4 )
printf("Left rotation of 0x%.8X by %d is 0x%.8X\n", n, d, leftRotate(n, d));
}
输出:
Left rotation of 0xA3519EBA by 0 is 0xA3519EBA
Left rotation of 0xA3519EBA by 4 is 0x3519EBAA
Left rotation of 0xA3519EBA by 8 is 0x519EBAA3
Left rotation of 0xA3519EBA by 12 is 0x19EBAA35
Left rotation of 0xA3519EBA by 16 is 0x9EBAA351
Left rotation of 0xA3519EBA by 20 is 0xEBAA3519
Left rotation of 0xA3519EBA by 24 is 0xBAA3519E
Left rotation of 0xA3519EBA by 28 is 0xAA3519EB
答案 1 :(得分:0)
确保您的号码在内存中的大小正确。这个C程序工作(并带有一个位打印功能,以帮助您调试/理解)。
#include <stdio.h>
#include <stdlib.h>
uint32_t print_4_bits(uint32_t x){
int i,res;
for(i=0;i<4;i++){
printf("%u",(x&0x80000000)>>31);
x=x<<1;
}
return x;
}
void print_bits(uint32_t x){
int i;
for(i=0;i<8;i++){
x=print_4_bits(x);
printf(" ");
}
}
uint32_t rol32(uint32_t n, uint32_t d){
return (n<<d)|(n>>(32-d));
}
int main(){
uint32_t n = 0xa3519eba;
uint32_t expected=0xcf5d51a8;
uint32_t d = 0xf;
print_bits(n);
printf("\n");
print_bits(rol32(n,d));
printf("\n");
if(rol32(n,d)!=expected)printf("boo\n");
}
这是输出:
1010 0011 0101 0001 1001 1110 1011 1010
1100 1111 0101 1101 0101 0001 1010 1000
更多解释:每种数据类型在内存中占用指定的空间量,而int可以具有不同的大小。此外,你正在对可能存在于两个补码表示法中的负数进行位移,因此移位可能会填充1,如本文所述:
Right shifting negative numbers in C
您可以通过强制执行32位无符号整数来解决内存大小问题和负数问题。在C(也可能是C ++)中使用uint32_t
。
至于你的实际比特,说你有数字0110 1100
并将它旋转2位,寻求1011 0001
作为解决方案。
从
开始 0110 1100
左移2个地方
1011 0000
&lt; -right大多数位用0填充。
右移8-2 = 6个地方
0000 0001
&lt; - left-most 6填充0。
现在,如果你按位|
,你可以从解决方案的每个部分得到你所关心的部分(那些你不关心的是括号内的部分)。
1011 00(00) | (0000 00)01 = 1011 0001
答案 2 :(得分:0)
通常情况下,存在一些现有的库函数,它使用短asm{}
块来执行适当的机器指令。事实上,每个CPU都知道如何进行滚动,但操作并未直接暴露在&#34; C&#34;语言。 &#34;(Shift&#34; 是的,&#34; roll&#34; 没有。)
答案 3 :(得分:0)
这是一个非常古老的问题,但现在终于在 C++20 中将旋转的基础 CPU 操作(由指令集支持了几十年)添加到 C++ 库中:std::rotl
。 (注意:它只为无符号整数类型实现,因为那些定义了行为。)
例如,您可以查看 the implementation by Microsoft。很难读,但基本上说:
template <typename T>
T rotl(T val, int rotationAmount) {
auto nrOfDigits = std::numeric_limits<T>::digits;
auto remainder = rotationAmount % nrOfDigits;
if (remainder > 0) {
return static_cast<T>(
static_cast<T>(val << remainder) | static_cast<T>(val >> (nrOfDigits - remainder)));
} else if (remainder == 0) {
return val; // no rotation
} else { // _Remainder < 0
return rotr(val, -remainder); // use rotate right
}
}
但是,您不应该自己实现这一点。如果我从上面的示例中查看 GCC 和 Clang 生成的程序集,它们会按字面意思实现代码(即使使用 -O3
):
sal esi, cl
mov ecx, 32
mov eax, ebx
sub ecx, edx
shr eax, cl
or esi, eax
调用 std::rotl
时给出
rol esi, cl
答案 4 :(得分:-1)
由于您不理解您所编写的内容如何工作,因此您有一个基本问题。不要试图简化解决方案。编写一个蛮力算法,一次向左旋转一位。对于d的每次迭代,保存每个最左边的位,向左移动,将每个位从左侧下降并将其添加到右侧。继续,直到你倒数到0。