为什么这个C算术移位实现不起作用?

为什么这个C算术移位实现不起作用?,c,bit-manipulation,C,Bit Manipulation,我正在尝试编写一个C函数,它将始终使用位运算符查找无符号整数参数的算术右移。我成功地编写了一个函数,该函数总是使用位运算符查找有符号整数的逻辑右移,但在查找算术移位方面似乎没有同样的想法 这是我现在的功能: unsigned arith(unsigned x, int k) { unsigned x_arith = (int) x >> k; x_arith = x_arith | ((-1<<(sizeof(x_arith)*8)-k)) &

我正在尝试编写一个C函数,它将始终使用位运算符查找无符号整数参数的算术右移。我成功地编写了一个函数,该函数总是使用位运算符查找有符号整数的逻辑右移,但在查找算术移位方面似乎没有同样的想法

这是我现在的功能:

unsigned arith(unsigned x, int k) {
    unsigned x_arith = (int) x >> k;

    x_arith = x_arith | ((-1<<(sizeof(x_arith)*8)-k)) & ~(-1<<(sizeof(x_arith)*8));
    return x_arith;
}
创建一个all-1的二进制文件,并将其移位,使第一个二进制文件在
x_-arth
中最左边的位之后对齐,然后翻转它,然后仅保留这些位与上面的二进制文件相同

整个过程假设创建了一个掩码,其中
x_arith
需要1。最后,我使用这个掩码
|
(或)原始的
x_算术
,以获得进行移位运算并返回结果所需的1

然而,结果仍然是逻辑上的转变。我试着在上面的代码中修改了很多东西,但我对这些奇怪的数字感到非常失望。我做错了什么

更新: 上面发布的函数实际上找到了算术移位。问题是我用4和k=1测试它,期望结果是6,结果不是算术移位的工作原理。以下是该函数的一个外观更好的版本,供将来参考:

unsigned arith_shift(unsigned x, int k) {

    unsigned x_arith = (int) x >> k;

    int bits = sizeof(int) * 8;

    unsigned mask = k? ((~0 << (bits-k)) & ~(~0 << bits)): 0;

    return x_arith | mask;

}
无符号算术移位(无符号x,整数k){
无符号x_arith=(int)x>>k;
整数位=sizeof(int)*8;
无符号掩码=k?(~0来自C99第4段:(强调)


  • E1的结果只需创建一个包含k个最左边1的掩码(如果数字为负数),这是通过算术移位完成的符号扩展:

    unsigned mask = x > 0x8000 ? (0xFFFF << (16-k)): 0;
    

    注意:我假设16位无符号,否则您需要使用sizeof()进行调整,因为这是我留给您的家庭作业

    您是否尝试对常规int而不是无符号进行移位?(或者在第一个移位中用括号括起铸造的x?)我不确定您想要实现什么以及为什么您认为它不起作用。--但至少
    sizeof(x_arith)*8
    是一个(无符号的)
    int
    的位数。因此,将
    -1
    按这个数字移位会导致(a)0或(b)未定义的行为。在can(a)中
    ~
    将再次为您提供
    -1
    。因此,这看起来没有什么用处。请看下面的表达式:
    (-1@ilombo No.@harper&@Tom)一个非常类似的过程使用-1将算术移位转换为逻辑移位。它只是
    xl=xl&(-1
    无符号x_arith=(int))而已x>>k;
    已经执行了算术右移。(假设
    (int)x
    的通常行为没有很好的定义)。代码的其余部分是干什么的?这似乎不起作用。我用4和1的移位尝试了这个函数,结果仍然是4(应该是6,对吧?).与-4类似。没有三元运算符,我得到-2147483644,就像我以前得到的一样:(不是6。让我们看看二进制应该发生什么:
    4=00000000 00000100
    shift right arith 1应该是:
    0->00000000 00000100=00000000 00000100
    仍然是4。算术移位扩展符号位,这是最左边的二进制位,在这种情况下,0会扩展为0。如果您有
    10000000 00000100
    (32722无符号,-32764有符号)那么按1移位将是:
    1->10000000000100=110000000010
    这是49154无符号(-16382有符号)@ilombo为什么将4(二进制0100)向右移位一次而不是2(二进制0010)对不起,你是对的,移位正4总是2。在我之前的评论中,移位1后的结果位应该是
    00000000000010
    谢谢你的回复,e0k。你有没有建议我可以尝试不同的方法?你的两个函数都工作了,但我都不能使用(第一个原因是我不理解,第二个原因是练习中说不要使用除法)。结果证明我发布的代码确实有效(见上文);我只是不知道如何正确测试它。既然你解释了我的代码可能不起作用的原因,我接受了你的答案。谢谢你的帮助。
    unsigned arith_shift(unsigned x, int k) {
    
        unsigned x_arith = (int) x >> k;
    
        int bits = sizeof(int) * 8;
    
        unsigned mask = k? ((~0 << (bits-k)) & ~(~0 << bits)): 0;
    
        return x_arith | mask;
    
    }
    
        int arith(int x, unsigned k) {
          asm volatile ("sar %1,%0" :"+r"(x) :"c"((unsigned char)k));
          return x;
        }
    
        int arith(int x, unsigned k) {
          if ( x >= 0 ) {
            // Same as integer division for nonnegative x
            return x/(1<<k);
          } else {
            // If negative, divide but round to -infinity
            return x/(1<<k) - ( x % (1<<k) == 0 ? 0 : 1 );
          }
        }
    
    unsigned mask = x > 0x8000 ? (0xFFFF << (16-k)): 0;
    
    unsigned x_arith = mask | (x >> k);