怎么样:
uint32_t moveNib(uint32_t x, int n) { return x<<(n<<2) | x>>((8-n)<<2); }
它用于<<2
从半字节转换为位,然后将位移动那么多。为了处理回绕,我们或通过在相反方向上移动了相反数量的数字的副本。例如,用x=0x87654321
and n=1
,左移 4 位变为0x76543210
,右移 28 位变为0x00000008
,并且当它们一起进行 OR 运算时,结果为0x76543218
。
编辑:如果-
真的不允许,那么这将得到相同的结果(假设一个具有二进制补码整数的架构)而不使用它:
uint32_t moveNib(uint32_t x, int n) { return x<<(n<<2) | x>>((9+~n)<<2); }
编辑2:好的。既然你不能使用任何东西int
,那么这个怎么样?
int moveNib(int x, int n) { return (x&0xffffffff)<<(n<<2) | (x&0xffffffff)>>((9+~n)<<2); }
逻辑与以前相同,但我们通过与 进行与运算来强制计算使用无符号整数0xffffffff
。不过,所有这些都假设为 32 位整数。我现在还有什么遗漏的吗?
Edit3:这是另一个版本,应该更便携:
int moveNib(int x, int n) { return ((x|0u)<<((n&7)<<2) | (x|0u)>>((9+~(n&7))<<2))&0xffffffff; }
n
它按照 chux 的建议设置上限,并用于|0u
转换为无符号数,以避免使用有符号整数获得符号位重复。之所以有效,是因为(根据标准):
否则,如果无符号整数类型的操作数的等级大于或等于另一个操作数类型的等级,则将有符号整数类型的操作数转换为无符号整数类型的操作数的类型。
由于int
和0u
具有相同的秩,但是0u
是无符号的,因此结果是无符号的,即使与 0 进行 ORing 否则将是一个空操作。
然后它将结果截断为 32 位的范围,int
以便如果 int 的位数超过此值,该函数仍然可以工作(尽管在这种情况下仍将在最低 32 位上执行轮换。64 位版本将用 15 替换 7,用 17 替换 9 并使用 0xffffffffffffffff 截断)。
此解决方案使用 12 个运算符(如果您跳过截断,则为 11 个,如果您存储n&7
在变量中,则为 10 个)。
要详细了解此处发生的情况,让我们通过您给出的示例来了解它:x=0x87654321
, n=1
. x|0u
结果是一个无符号数0x87654321u
。(n&7)<<2=4
,所以我们将左移 4 位,而((9+~(n&7))<<2=28
,所以我们将右移 28 位。所以把这些放在一起,我们将计算0x87654321u<<4 | 0x87654321u >> 28
。对于 32 位整数,这是0x76543210|0x8=0x76543218
. 但是对于 64 位整数,它是0x876543210|0x8=0x876543218
,所以在这种情况下,我们需要截断为 32 位,这就是 final&0xffffffff
所做的。如果整数短于 32 位,那么这将不起作用,但是您在问题中的示例有 32 位,所以我假设整数类型至少有那么长。
作为一个小旁注:如果您允许一个不在列表中的运算符,即sizeof
运算符,那么我们可以制作一个自动处理更长 int 的所有位的版本。受 Aki 启发,我们得到(使用 16 个运算符(记住,sizeof
是 C 中的运算符)):
int moveNib(int x, int n) {
int nbit = (n&((sizeof(int)<<1)+~0u))<<2;
return (x|0u)<<nbit | (x|0u)>>((sizeof(int)<<3)+1u+~nbit);
}