C 负数:如何将有符号整数中的符号位更改为0?

C 负数:如何将有符号整数中的符号位更改为0?,c,bit-manipulation,C,Bit Manipulation,我认为这个世界是可行的,但事实并非如此: int a = -500; a = a << 1; a = (unsigned int)a >> 1; //printf("%d",a) gives me "2147483148" inta=-500; a=a>1; //printf(“%d”,a)给了我“2147483148” 我的想法是左移位将删除最左边的符号位,因此将其作为无符号int进行右移位将保证它是逻辑移位而不是算术移位。为什么这是不正确的 此外: inta=-5

我认为这个世界是可行的,但事实并非如此:

int a = -500;
a = a << 1;
a = (unsigned int)a >> 1;
//printf("%d",a) gives me "2147483148"
inta=-500;
a=a>1;
//printf(“%d”,a)给了我“2147483148”
我的想法是左移位将删除最左边的符号位,因此将其作为无符号int进行右移位将保证它是逻辑移位而不是算术移位。为什么这是不正确的

此外:

inta=-500;

a=aTL;DR:最简单的方法是使用
中的
abs
功能。答案的其余部分涉及在计算机上表示负数

负整数(几乎总是)表示为。(见下文注释)

获取数字负数的方法是:

  • 取整数的二进制表示形式(包括数据类型的前导零,但用作符号位的MSB除外)
  • 以上面的数字为例
  • 1
    添加到1的补码中
  • 前缀a
  • 500
    为例

  • 500
    的二进制表示形式为例:
    \u 000 0001 1111 0100
    \u
    是符号位的占位符)
  • 取其1的补/反:
    \u 111 1110 0000 1011
  • 1
    添加到1的补码中:
    \u 111 1110 0000 1011+1=\u 111 1110 0000 1100
    。这与将符号位替换为零时获得的
    2147483148
    相同
  • 前缀
    0
    表示正数,前缀
    1
    表示负数:
    1111110 0000 1100
    。(这将不同于上面的
    2147483148
    。获取上述值的原因是您对MSB进行了核弹处理)
  • 反转符号也是一个类似的过程。如果您使用16位或32位数字引导您看到的大值,则会得到前导数字。在每种情况下,LSB应相同


    注意:有些机器使用1的补码表示,但它们是少数。2的补码通常是首选的,因为
    0
    具有相同的表示,即
    -0
    0
    在2的补码表示法中表示为全零。

    左移负整数调用未定义的行为,所以不能这样做。如果您在“还”部分中输入了
    a=(unsigned int)a,则可以使用代码,在第一次左移1位后,a确实会像预期的那样反映-1000

    问题在于对unsigned int的强制转换。如上所述,负数表示为2的补码,这意味着符号由最左边的位(最高有效位)确定。当强制转换为无符号整数时,该值不再表示符号,而是增加整数可以接受的最大值

    假设32位整数,MSB过去表示-2^31(=2147483648),现在表示无符号整数中的正2147483648,增加2*2147483648=4294967296。将此值与原始值-1000相加,得到4294966296。右班除以2,到达2147483148

    希望这可能会有所帮助:(修改打印功能自)

    void int2bin(int a,char*buffer,int buf_size){
    缓冲区+=(buf_大小-1);
    对于(int i=buf_size-1;i>=0;i--){
    *缓冲区--=(a&1)+“0”;
    a>>=1;
    }
    }
    int main(){
    int检验=-500;
    int bufSize=sizeof(int)*8+1;
    字符buf[bufSize];
    buf[bufSize-1]='\0';
    int2bin(测试,buf,bufSize-1);
    printf(“%i(%u):%s\n”,test,(unsigned int)test,buf);
    //打印:-500(4294966796):11111111111111 00000 1100
    
    test=test它确实有效,它将符号设置为0。但可能不是您想要的方式。如果您想用bitmath实现
    abs
    ,它比这要复杂一些。公平地说,将标准
    abs
    用于MIN_INT有点危险。不是固有的,而是溢出和UB-yada-yada..您可以只做一个无符号的
    abs <代码> >(x<0)返回(未签名)x;否则返回(未签名)x;< /C> > C和C++,<代码> A,因为您要求更改<代码>符号>代码>位(x86 ASM操作码:代码>未/代码>),并且不将负数转换成正数(x86 ASM操作码:<代码> NEG < /代码>)。这是两个不同的东西。下面的答案解释了这一点,并且是正确的。@Hurkyl有区别吗?在任何情况下,如果有区别,它在这里都不可见。在您的示例中,左移31是UB,因为2^31在int32_t中不可表示。编辑:假设int与int32_t具有相同的表示。@2501
    lu
    后缀应该是什么没有必要,因为有断言。对,我忘记了。但是,int可能有两个填充字节。:-)sizeof不计算范围。你应该使用int\u MAX>=2^31-1。lu避免了所有这些。我正在处理gnss坐标,需要我找出它是0还是-0?我如何识别-0?
    int a = -500;
    a = a << 1;
    //printf("%d",a) gives me "-1000"
    
    (int32_t)a & ~(1u << 31)
    
    #include <stdio.h>
    #include <stdint.h>
    
    int main (void)
    {
      int a = -500;
      a = (unsigned int)a << 1;
      a = (unsigned int)a >> 1;
      printf("%.8X = %d\n", a, a);
    
      _Static_assert(sizeof(int)>=4, "Int must be at least 32 bits.");
      a = -500;
      a = (int32_t)a & ~(1u << 31);
      printf("%.8X = %d\n", a, a);
    
      return 0;
    }
    
    void int2bin(int a, char *buffer, int buf_size) {
        buffer += (buf_size - 1);
    
        for (int i = buf_size-1; i >= 0; i--) {
            *buffer-- = (a & 1) + '0';
    
            a >>= 1;
        }
    }
    
    int main() {
        int test = -500;
        int bufSize = sizeof(int)*8 + 1;
        char buf[bufSize];
        buf[bufSize-1] = '\0';
        int2bin(test, buf, bufSize-1);
        printf("%i (%u): %s\n", test, (unsigned int)test,  buf);
        //Prints:   -500 (4294966796): 11111111111111111111111000001100
    
    
        test = test << 1;
        int2bin(test, buf, bufSize-1);
            printf("%i (%u): %s\n", test, (unsigned int)test, buf);
        //Prints:   -1000 (4294966296): 11111111111111111111110000011000
    
    
        test = 500;
        int2bin(test, buf, bufSize-1);
        printf("%i (%u): %s\n", test, (unsigned int)test, buf);
        //Prints:   500 (500): 00000000000000000000000111110100
    
    
        return 0;
    }