为什么将整数左移 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;
}
左移有符号整数是可以的,但右移是实现定义的。许多系统使用算术右移,结果与使用按位右移的结果不同:
- 您正在向后存储数字
- 您正在使用带符号的 int32,而移动 23 个结果需要超过 32 位来处理该操作...您应该使用 long long int
- 有符号整数可能导致错误答案,因为 1<<31 是 -1,这会导致字符串中出现错误字符
最后使用 unsigned long long int 以正确的顺序存储数字将产生正确的字符串
- 在看到您的代码的这个改进版本之前,您应该尝试自己重新编写代码
#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;
}
我尝试将 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;
}
左移有符号整数是可以的,但右移是实现定义的。许多系统使用算术右移,结果与使用按位右移的结果不同:
- 您正在向后存储数字
- 您正在使用带符号的 int32,而移动 23 个结果需要超过 32 位来处理该操作...您应该使用 long long int
- 有符号整数可能导致错误答案,因为 1<<31 是 -1,这会导致字符串中出现错误字符 最后使用 unsigned long long int 以正确的顺序存储数字将产生正确的字符串
- 在看到您的代码的这个改进版本之前,您应该尝试自己重新编写代码
#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;
}