I'm curious as to peoples thoughts on this small library I have made. I noticed there are no functions in the C standard library to accomplish this task.
I have tried to make the code machine independent. If anyone has access to machines where short != 2 byte, int != 4 byte, long != 8 byte or where bytes themselves are not 8 bits. I would be curious as to the test results.
The current functions for printing the binary representations are all slight modifications of the below:
#include <limits.h>
#include <stdio.h>
void printcb(char n)
{
int i;
int type_bits = CHAR_BIT;
int mask = 1 << type_bits - 1;
for (i = 0; i < type_bits; ++i) {
if (n & mask)
putc('1', stdout);
else
putc('0', stdout);
n <<= 1;
}
putc('\n', stdout);
}
2 Answers 2
The left shift operator on signed types is tricky and will easily lead you to undefined behavior.
To put it formally, here is a quote from §6.5.7/4 Bitwise shift operators of a C11 draft, emphasis added:
The result of
E1 << E2
is E1 left-shifted E2 bit positions; vacated bits are filled with zeros. If E1 has an unsigned type, the value of the result is E1 ×ばつ 2E2 , reduced modulo one more than the maximum value representable in the result type. If E1 has a signed type and nonnegative value, and E1 ×ばつ 2E2 is representable in the result type, then that is the resulting value; otherwise, the behavior is undefined.
char
is signed on a lot of platforms (likely including on the one you're testing). Therefore the following expression can fall foul of the "non-negative" constraint in the standardese above:
n <<= 1;
If n
is negative, the expression has undefined behavior, even on common platforms with boring 8-bit (signed) chars.
There's a problem with the following too, on stranger implementations, related to the second constraint:
int mask = 1 << type_bits - 1;
On a platform where sizeof(int) == sizeof(char)
(possible with e.g. CHAR_BITS==16
implementations, see What platforms have something other than 8-bit char? for examples), that shift will push the 1
into the sign bit, which has undefined behavior: 1 x 2CHAR_BITS-1 is not representable (at least in 2's complement, 1's complement or sign/magnitude representations – they stop at 2CHAR_BITS-1-1. No idea if there are other representations that fulfill the C standard's conditions and would be able to represent that).
TLDR: unsigned types are the bit-twiddler's friend.
You can just call
putchar()
, which automatically prints tostdout
:putchar('1');
The conditionals can instead be a ternary statement within
putchar()
:putchar(n & mask ? '1' : '0');
putchar
is often better suited for single-character output. putchar
only has to pass a character directly to the output. This particular code isn't exactly performance-critical but it's useful to acquire good coding habits from the beginning.