[英]print binary representation of a number
我想打印一个int
的二进制表示。 我的解决方案似乎适用于Visual Studio中的int
和unsigned int
,但是有人告诉我这是错误的。 有人看到错误吗? 如果是这样,为什么我的程序似乎对我有用?
void printbin(int n)
{
unsigned int i = 1<<31;
for (int j=0; j<32; j++)
{
if ((n & i) != 0)
printf("1");
else
printf("0");
i = i>>1;
}
printf("\n");
}
为什么我的程序似乎对我有用?
有两种非独家可能性:
从未定义的行为开始:首先观察@chux,评估表达式1<<31
会在具有32位(或更小) int
的系统上产生未定义的行为,例如Windows和Visual Studio的C编译器提供的行为。 两个操作数都是int
类型,因此结果是int
类型,但是算术上正确的结果超出了可以由该类型表示的值范围。 在这种情况下的行为将为无符号整数结果定义,但对于有符号整数类型(如int
,它是显式未定义的。 由于您将结果分配给unsigned int
类型的变量,因此只需将表达式更改为1u<<31
即可解决该问题。
此外,未指定任何类型表示中的位数,但您的代码假定为32位unsigned int
。 这确实是Visual Studio C编译器提供的unsigned int
的大小,但您不需要依赖它。 通过将unsigned int
表示中的位数计算为CHAR_BIT * sizeof(unsigned int)
您将获得每个环境的正确的依赖于实现的结果。
但是,只要我们讨论实现依赖性,就不一定是对象表示中的所有位都有助于其值的情况。 也可以有填充位,并且在其unsigned int
类型的表示中具有少于32个值位的实现上,表达式1u << 31
或等效值评估为零。 为了完全正确,计算unsigned int
表示中的值位数必须基于UINT_MAX
的值。 您创建的用于回避此问题的位掩码的替代表达式为~(UINT_MAX >> 1)
。
至于输出格式,不清楚int
的“二进制”形式是什么,特别是考虑到你想要提供负值和正值。 如果您应该在不使用-
符号的情况下提供负值的表单,就像您的代码尝试那样,那么必须指定或假设所需输出形式的详细信息(例如,big-endian,32位二进制补码) ,或者您打算探测输入值的机器特定表示。 由于您没有指定特定格式,如果(部分)问题出在输出格式中,那么我只能得出结论是需要特定于机器的表示或符号/幅度。
如果目标是探测int
值的机器表示,那么您的程序至少在两个(附加)计数上是不正确的。
首先,计算表达式n&i
涉及转换的值i
从类型int
输入unsigned int
。 因此,您打印的是转换值的表示,不保证与原始int
值的表示相同。 但实际上,你不可能遇到存在实际差异的机器和C实现。 当然,Windows上的Visual Studio不是这样的环境。
但是,您的代码输出的值的逻辑表示不一定符合物理表示。 即使假设您没有遇到各种对象表示的转换或大小等问题,您的代码也假定物理布局是从最重要的字节到最不重要的字节。 也就是说,它打印一个大端表示,而不管实际的物理表示。 在x86和x86_64,原生物理表示int
s是小 -endian,以下我的代码打印的机器表示将打印不同的结果比你的代码做。
void printbin(int n)
{
unsigned char *p = (unsigned char *) &n;
for (int j=0; j < sizeof(n); j++)
{
for (unsigned char mask = 1u << (CHAR_BIT - 1); mask; mask >>= 1) {
putchar((*p & mask) ? '1' : '0');
}
p += 1;
}
putchar('\n');
}
该标准允许不同指针类型之间的转换,并且它特别认为该程序中的转换将导致p
被初始化为指向n
的表示中的第一个字节。 程序逐步执行表示中的每个字节(通过sizeof
运算符确定的总数)并打印每个字节中的位,从最重要到最不重要,与您的版本类似。 如果有填充位,则包括它们。
另一方面,如果您想要一个带符号的二进制数字字符串,从最重要的非零位到最低有效位,那么您可以这样做:
void printbin_digits(unsigned int n) {
char bits[CHAR_BIT * sizeof(unsigned int)] = {0};
int bit_count = 0;
while (n) {
bits[bit_count++] = n % 2;
n >>= 1;
}
while (bit_count) {
putchar(bits[--bit_count] ? '1' : 0);
}
}
void printbin(int n)
{
if (n == 0) {
putchar('0');
} else if (n == INT_MIN) {
putchar('-');
printbin_digits(-(n / 2));
putchar((n % 2) ? '1' : '0');
} else if (n < 0) {
putchar('-');
printbin_digits(-n);
} else {
printbin_digits(n);
}
putchar('\n');
}
这对于没有C类标准支持的int
类型值的表示没有任何假设。 特别注意当n
具有值INT_MIN
时的特殊处理 - 它很麻烦,但它是必要的,因为计算表达式-INT_MIN
可以(并且在x86上)会产生未定义的行为。
1<<31
移位一位通过值位并可能移位到符号(或填充)位。 这是C中未定义的行为。
n & i
试图“和” unsigned int
位和signed int
的signed int
。
OP使用32假设int
是32位宽。
以下是打印符号和可变位数的示例 - 工作[INT_MIN...INT_MAX]
。
#include <limits.h>
void printbin_c(int n) {
char buf[CHAR_BIT * sizeof n + 1];
char *p = &buf[sizeof buf - 1];
*p = '\0';
int i = n;
if (i > 0) {
i = -i;
}
do {
p--;
*p = '0' - i%2;
i /= 2;
} while (i);
if (n < 0) {
p--;
*p = '-';
}
puts(p);
}
[编辑]应对1的补充@John Bollinger
使用负绝对值if (i > 0) i = -i;
因为正绝对值不适用于INT_MIN
2的补码。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.