按位向右旋转4位值

NT9*_*T93 23 c bit-manipulation motordriver

我正在尝试使用简单的完整步骤来控制步进电机.这意味着我正在输出一系列值,如下所示:

1000
0100
0010
0001
Run Code Online (Sandbox Code Playgroud)

我认为一个简单的方法是只取我的4位值,然后在每一步之后执行右旋操作."代码"显然不遵循任何语法,它只是用来说明我的想法:

step = 1000;
//Looping
Motor_Out(step)
//Rotate my step variable right by 1 bit
Rotate_Right(step, 1)
Run Code Online (Sandbox Code Playgroud)

我的问题是,显然我没有任何4位简单数据类型可以用于此,如果我使用8位无符号整数,我最终将1关闭到MSB,这意味着4位我真正感兴趣的价值,将转为0000几步.

我已经读过你可以使用结构和位字来解决这个问题,但我从中读到的大部分内容都告诉我这是一个非常糟糕的主意.

250*_*501 21

只有4个可能的值,您将使用包含9个元素的表:

unsigned char table_right[] = { [0x1] = 0x8 , [0x2] = 0x1 , [0x4] = 0x2 , [0x8] = 0x4 };
Run Code Online (Sandbox Code Playgroud)

当您需要下一个值时,只需使用当前值作为索引:

unsigned char current = 0x4;    //value is: 0b0100
unsigned char next = table_right[current];  //returns: 0b0010
assert( next == 0x2 );
Run Code Online (Sandbox Code Playgroud)

在循环中执行此操作将循环遍历所有四个可能的值.

方便的是,传递一个无效值将返回一个零,所以你可以编写一个get函数,该函数也会在下一个断言!= 0.你还应该在将值传递给数组之前断言值<9.

  • 为什么你会使用查找表,当有很多琐碎的算术解决方案,如`next =((current&1)<< 3)| (当前>> 1)`?您不仅在查找表中浪费空间,而且在大多数系统上,内存提取可能比几个按位操作慢得多. (8认同)
  • @IlmariKaronen感谢您的意见,这也是一种有效的方法.请不要低估正确的答案,而是像我一样向其他人使用你的方法,因为我发现他们比我的更好. (3认同)

Sea*_*ean 15

只需使用an int来保存值.旋转时,将最低有效位复制到第4位,然后将其右移1:

int rotate(int value)
{
    value |= ((value & 1) << 4); // eg 1001 becomes 11001
    value >>= 1;                 // Now value is 1100
    return value;
}
Run Code Online (Sandbox Code Playgroud)


Kev*_*inZ 10

对此的算法很简单,它总是比表格方法更快:

constexpr unsigned rotate_right_4bit ( unsigned value )
{
    return ( value >> 1 ) | ( ( value << 3 ) & 15 );
}
Run Code Online (Sandbox Code Playgroud)

这将变为5行无分支x86汇编:

lea     eax, [0+rdi*8]
shr     edi
and     eax, 15
or      eax, edi
ret
Run Code Online (Sandbox Code Playgroud)

或者,或者,如果您确实喜欢查看索引{3, 2, 1, 0},那么您可以将它们拆分为2个函数,一个"递增"索引,另一个实际计算值:

constexpr unsigned decrement_mod4 ( unsigned index )
{
    return ( index - 1 ) & 3;
}

constexpr unsigned project ( unsigned index )
{
    return 1u << index;
}
Run Code Online (Sandbox Code Playgroud)


bor*_*sbn 8

IMO最简单的方法是:

const unsigned char steps[ 4 ] = { 0x08, 0x04, 0x02, 0x01 };
int stepsIdx = 0;
...
const unsigned char step = steps[ stepsIdx++ ];
stepsIdx = stepsIdx % ( sizeof( steps ) / sizeof( steps[ 0 ] ) );
Run Code Online (Sandbox Code Playgroud)

  • @UKMonkey:不,不会.查看将`stepsIdx`剪辑到范围内的最后一行?(我个人认为它是`stepsIdx&= 3;`但很可能,一个好的优化编译器会为你做这件事.) (5认同)
  • 并使其未来证明:`stepsIdx = stepsIdx%sizeof(步骤);`(如果你改变了类型,可能添加`/ sizeof(steps [0])` (2认同)

nin*_*wei 7

你可以使用10001000b和mod10000b

你可以01000100b 00100010b 00010001b 10001000b重复一遍.

例如:

char x = 0x88;
Motor_Out(x & 0xf);
Rotate_Right(step, 1);
Run Code Online (Sandbox Code Playgroud)

  • 请不要仅用代码发布答案.尝试提供解释. (3认同)

ilk*_*chu 7

如果我使用8位无符号整数,我最终会将1关闭到MSB

因此,当值变为零时,使用移位并重新初始化所需的位.C无论如何都没有旋转操作,所以你必须至少做两个班次.(我想C++也没有旋转.)

x >>= 1;
if (! x) x = 0x08;
Run Code Online (Sandbox Code Playgroud)

简单,写作简短,并且显而易见.是的,它将编译成一个分支(除非处理器有一个条件移动操作),但是直到你有分析器输出来告诉你这很重要,你只需要花费更多的时间来考虑它,而不是那些处理器周期.