Print binary representation of a number

I want to print a binary representation of an int . My solution seems to work for both int and unsigned int in Visual Studio, but someone told me this is wrong. Does anyone see a mistake? If so, why is my program working for me?

 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"); } 
+5
source share
2 answers

Why does my program work for me?

There are two non-exclusive options:

  • Your program works correctly for all inputs and conditions that you tested, but there are inputs and / or conditions that you have not tested on which it does not work. As a special case, the complaint may be that your program depends on undefined, implementation, or undefined behavior (what it does), which makes it inherently incorrect even if it works as you would expect in your test environment.
  • You are mistaken in the correct operation of your program, presumably as a result of a misunderstanding regarding the desired result.

Undefined / implementation-defined behavior

Starting with undefined behavior: when @chux is first observed, evaluating the expression 1<<31 causes undefined behavior on systems with 32-bit (or less) int s, for example, provided by Windows and the Visual Studio C Compiler. Both operands are of type int , therefore the result is of type int , but the arithmetically correct result goes beyond the values ​​that can be represented by this type. The behavior in this case will be defined for an unsigned integer result, but clearly undefined for integer integer types such as int . Since you are assigning the result to a variable of type unsigned int , you can fix this problem by simply changing the expression to 1u<<31 .

Also, the number of bits in a view of any type is not specified, but your code assumes a 32-bit unsigned int . This is indeed the size of the unsigned int provided by the Visual Studio C compiler, but you do not need to depend on it. You will get the correct implementation-dependent result for each environment by calculating the number of bits in the unsigned int representation as CHAR_BIT * sizeof(unsigned int) .

However, while we are talking about implementation dependencies, it is not necessary that all the bits in the representation of the object contribute to its value. There may also be bits of additions, and in an implementation that has less than 32 bits of value in its representation of type unsigned int , the expression 1u << 31 or the equivalent is evaluated to zero. To be completely correct, the calculation of the number of bits of a value in an unsigned int representation must be based on the value of UINT_MAX . An alternative expression for the generated bitmask that will ~(UINT_MAX >> 1) this problem would be ~(UINT_MAX >> 1) .

Output format

As for the output format, it is unclear what the binary form int , especially if you want to specify both negative and positive values. If you must provide a form for negative values ​​without the use of the sign - as your code does, then you must specify or accept the details of the required output form (for example, big-endian, 32-bit two addition), otherwise you must examine the machine-specific representation input values. Since you did not specify a specific format, if (part of) the problem lies in the output format, then I can only conclude that either a machine representation or a sign / value is required.

Performance car

If the goal is to define a machine representation of int values, then your program will be incorrect for at least two (additional) counters.

First, evaluating the expression n&i involves converting the value of i from int to unsigned int . What you type in is a representation of the converted value, which is not guaranteed to be the same as the representation of the original int value. In practice, however, it is unlikely that you will ever come across a machine and a C implementation where there is a real difference. Of course, Visual Studio on Windows is not such an environment.

In addition, however, your code displays a logical representation of the value, which does not necessarily correspond to the physical representation. Even assuming you don’t have problems with conversions or with sizes, etc. Of various representations of objects, your code assumes that the physical layout is from the most significant to the least significant byte. That is, he prints a representation of a large number, regardless of the actual physical representation. On x86 and x86_64, your own int physical representation is not suitable, and my code below will print different results than your code to print the machine view.

 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'); } 

The standard allows conversions between different types of pointers, and it definitely believes that the conversion in this program will initialize p to point to the first byte in the representation n . The program executes every byte in the view (the total amount of which is determined using the sizeof operator) and prints a bit in each of them, from the most significant to the least significant, similar to your version. If there are padding bits, they are turned on.

Sign / quantity representation

If, on the other hand, you need a signed binary numeric string, from the most significant nonzero bit to the least significant bit, then you can do this as follows:

 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'); } 

This works without any assumptions about representing int values ​​that are not supported by C. Note, in particular, special handling when n is INT_MIN - it is messy, but it is necessary because evaluating the expression -INT_MIN can ( and on x86) to produce undefined behavior.

+2
source

1<<31 shifts the bit, passing the bits of the value and potentially to the sign (or pad) bit. This behavior is undefined in C.

n & i tries the "and" bits of a unsigned int and the sign of a signed int .

Using an OP of 32 assumes that int is 32 bits wide.

Below is an example that prints a character and a variable number of bits - it works [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); } 

[Edit] Right with 1 add-on @ John Bollinger

Using a negative absolute value with if (i > 0) i = -i; since a positive absolute value does not work well with the INT_MIN 2 complement.

+2
source

All Articles