为什么将整数左移 24 位会产生错误的结果?

Why does left-shifting an integer by 24-bit yield the wrong result?

我尝试将 32 位整数左移 24:

char *int_to_bin(int num) {
    int i = 0;
    static char bin[64];
   
    while (num != 0) {
        bin[i] = num % 2 + 48;
        num /= 2;
        i++;
    }
    bin[i] = '[=10=]';
    return (bin);
}

int main() {
    int number = 255;
    printf("number: %s\n", int_to_bin(number));
    printf("shifted number: %s\n", int_to_bin(number << 24));
    return 0;
}

输出:

number: 11111111
shifted number: 000000000000000000000000/

我用 23 位左移它产生了这个结果:

0000000000000000000000011111111

为什么会这样,错误结果末尾的'/'是怎么回事?

两件事:

  • 如果number的值为255,则number << 24的数值为4278190080,溢出一个32位有符号整数,其最大可能值为2147483647。有符号整数溢出在 C 中是未定义的行为,所以结果可能是任何东西。

    在这种情况下可能发生的情况是移位的结果是负数。当num为负数时,num % 2可能取值-1,所以你在字符串中存储字符47,即/.

    移位数学通常更适合 unsigned 类型,其中溢出是明确定义的(它环绕并且位只是从左边移开并消失)并且 num % 2 只能是0 或 1。(或改为 num & 1。)

  • 您的 int_to_bin 例程将最低有效位放在字符串的开头(左侧),因此结果与人们通常书写数字的方式相反(使用右边的最低有效位)。你可能想重写它。

Shift 工作正常,您只是从错误的方向打印它。

char *int_to_bin(char *buff, int num)
{
    unsigned mask = 1U << (CHAR_BIT * sizeof(num) - 1);
    char *wrk = buff;
    for(; mask; mask >>= 1)
    {
        *wrk++ = '0' + !!((unsigned)num & mask);
    }
    *wrk = 0;
    return buff;
}

int main()
{
    char buff[CHAR_BIT * sizeof(int) + 1];
    int number = 255;
    printf("number: %s\n", int_to_bin(buff, number));
    printf("shifted number: %s\n", int_to_bin(buff, number << 24));
    return 0;
}

左移有符号整数是可以的,但右移是实现定义的。许多系统使用算术右移,结果与使用按位右移的结果不同:

https://godbolt.org/z/e7f3shxd4

  1. 您正在向后存储数字
  2. 您正在使用带符号的 int32,而移动 23 个结果需要超过 32 位来处理该操作...您应该使用 long long int
  3. 有符号整数可能导致错误答案,因为 1<<31 是 -1,这会导致字符串中出现错误字符 最后使用 unsigned long long int 以正确的顺序存储数字将产生正确的字符串
  4. 在看到您的代码的这个改进版本之前,您应该尝试自己重新编写代码

#include<stdio.h>
#include<stdlib.h>
char *int_to_bin( unsigned long long   int num) {
    int i = 0;
    static char bin[65];


    while (i != 64) {
        bin[63-i] = num % 2 + 48;
        num /= 2;
        i++;
    }
    bin[64] = '[=10=]';

    return (bin);
}

int main() {
    unsigned long long  int number = 255;
    printf("number 1: %s\n", int_to_bin(number));
    printf("number 2: %s\n", int_to_bin(number << 24));
    return 0;
}