print bits of unsigned value

M

Mark

I need to print out a bits layout of 32-bit register, where bits 0-24
represent a network port's status (bit 1 is for up and 0 for down) and
others are not used. Simply printing each bit in a loop works fine, but I'd
like to write it in a buffer with formatted output, and this where I stuck:

puts("Group Ports");
puts(" 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
22 23 24");
puts("-----------------------------------------------------------------------------");

uint32_t val = 0x1234567; /* for this example */
char buf[66] = " "; /* 6 spaces */
int i, m = 0;

for (i = 0; i < sizeof(val) * CHAR_BIT; i++) {
/* 10 is left margin of spaces in the above string */
/* I have to smoothly move along the buffer to print 1 or 0
according to the port's number */

m += snprintf(buf + 10 + m, sizeof(buf), (val & 0x00000001) ? "1 " :
"0 "); /* XXX */
val = val << 1;
}
fprintf(stdout, "%s\n", buf);

This code is very clumsy and, in my understanding, XXX invokes underfined
behavior, because 'm' is used in two sequence points?
What would you recommend to improve/change to make it look clean and solid?
 
B

bart.c

Mark said:
I need to print out a bits layout of 32-bit register, where bits 0-24
represent a network port's status (bit 1 is for up and 0 for down) and
others are not used. Simply printing each bit in a loop works fine, but I'd
like to write it in a buffer with formatted output, and this where I stuck:

puts("Group Ports");
puts(" 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
22 23 24");
puts("-----------------------------------------------------------------------------");

uint32_t val = 0x1234567; /* for this example */
char buf[66] = " "; /* 6 spaces */
int i, m = 0;

for (i = 0; i < sizeof(val) * CHAR_BIT; i++) {
/* 10 is left margin of spaces in the above string */
/* I have to smoothly move along the buffer to print 1 or 0
according to the port's number */

m += snprintf(buf + 10 + m, sizeof(buf), (val & 0x00000001) ? "1 "
: "0 "); /* XXX */
val = val << 1;
}
fprintf(stdout, "%s\n", buf);

Unless you are doing something else with 'buf', you might as well output the
bits immediately:

printf("%d ",val & 1); /* XXX */
 
B

Ben Bacarisse

Mark said:
I need to print out a bits layout of 32-bit register, where bits 0-24
represent a network port's status (bit 1 is for up and 0 for down) and
others are not used. Simply printing each bit in a loop works fine,
but I'd like to write it in a buffer with formatted output, and this
where I stuck:

puts("Group Ports");
puts(" 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
20 21 22 23 24");
puts("-----------------------------------------------------------------------------");

uint32_t val = 0x1234567; /* for this example */
char buf[66] = " "; /* 6 spaces */
int i, m = 0;

for (i = 0; i < sizeof(val) * CHAR_BIT; i++) {
/* 10 is left margin of spaces in the above string */
/* I have to smoothly move along the buffer to print 1 or 0
according to the port's number */

m += snprintf(buf + 10 + m, sizeof(buf), (val & 0x00000001) ?
"1 " : "0 "); /* XXX */
val = val << 1;
}
fprintf(stdout, "%s\n", buf);

This code is very clumsy and, in my understanding, XXX invokes
underfined behavior, because 'm' is used in two sequence points?
What would you recommend to improve/change to make it look clean and
solid?

(1) 66 is not enough space if the 0/1s are to line up under the numbers.
(2) You start with 6 spaces and then skip 10 places.
(3) val = val << 1; is not right. You probably mean val = val >> 1;
(4) The snprintf call is wrong: the space available is not sizeof(buf)
because you pass an ever increasing pointer to it.
(5) You seem to be putting the 0/1s into consecutive places (i.e. with
no space between them).
(6) The 0/1 won't line up under printed number unless you move on two
spaces for bit numbers > 9.
(7) Even if you fix number 2, you never null termiate the string that
you print in the last line.
(8) I'd write 1 rather than 0x00000001. They mean the same thing.
(9) You loop (and therefore write) at least 32 bits rather than 24.
(10) Why are you using a buffer rather than just printing the bits?

The biggest suggestion I have is not to use a buffer but that seems to
be built into your question for some reason. Why?

If I had to use a buffer, I'd:
(a) make it big enough (indent + 23 + 24 + 24 - 9 + 1) bytes;
(b) fill it with spaces and null terminate it;
(c) put the 0/1s into place using something like

buf[indent + i + (i > 9)] = val & 1 ? '1' : '0';
 
I

ImpalerCore

I need to print out a bits layout of 32-bit register, where bits 0-24
represent a network port's status (bit 1 is for up and 0 for down) and
others are not used. Simply printing each bit in a loop works fine, but I'd
like to write it in a buffer with formatted output, and this where I stuck:

puts("Group     Ports");
puts("               1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
22 23 24");
puts("-----------------------------------------------------------------------------");

uint32_t val = 0x1234567;  /* for this example */
char buf[66] = "      ";  /* 6 spaces */
int i, m = 0;

for (i = 0; i < sizeof(val) * CHAR_BIT; i++) {
        /* 10 is left margin of spaces in the above string */
        /* I have to smoothly move along the buffer to print 1 or 0
according to the port's number */

        m += snprintf(buf + 10 + m, sizeof(buf), (val & 0x00000001) ? "1 " :
"0 ");   /* XXX */
        val = val << 1;}

fprintf(stdout, "%s\n", buf);

This code is very clumsy and, in my understanding, XXX invokes underfined
behavior, because 'm' is used in two sequence points?
What would you recommend to improve/change to make it look clean and solid?

You can try my 'c_bit_snprintf' I posted back on Apr 1. It may not do
exactly what you want, but it's able to generate space separated
patterns of bits. There's no control flags to specify the amount of
spacing though It's limited to a single space, but it should be
trivial to change the spacing code EMIT_C( ' ' ) into what you need.

http://groups.google.com/group/comp.lang.c/msg/466b3800e12cfb62

Best regards,
John D.
 
B

bart.c

....

Since it doesn't appear you have tested your code, here is some ready-made
code that seems to work:

#include <stdio.h>

int main(void){
#define NBITS 24
int i;
int val=0x87654321;

printf ("Group Ports\n");
printf (" ");

for(i=1; i<=NBITS; ++i)
printf("%3d",i);

printf ("\n ");

for(i=1; i<=NBITS; ++i) {
printf("%3d",val&1);
val = val>>1;
}
printf("\n");

}
 
M

Mark

[skip]
The biggest suggestion I have is not to use a buffer but that seems to
be built into your question for some reason. Why?

If I had to use a buffer, I'd:
(a) make it big enough (indent + 23 + 24 + 24 - 9 + 1) bytes;
(b) fill it with spaces and null terminate it;
(c) put the 0/1s into place using something like

buf[indent + i + (i > 9)] = val & 1 ? '1' : '0';

I've thought it through and it appears to me now I don't need a buffer at
all, simple printf would do the job just fine.
Thanks, Ben & Bart.
 
B

Barry Schwarz

I need to print out a bits layout of 32-bit register, where bits 0-24
represent a network port's status (bit 1 is for up and 0 for down) and
others are not used. Simply printing each bit in a loop works fine, but I'd
like to write it in a buffer with formatted output, and this where I stuck:

puts("Group Ports");
puts(" 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
22 23 24");
puts("-----------------------------------------------------------------------------");

uint32_t val = 0x1234567; /* for this example */
char buf[66] = " "; /* 6 spaces */
int i, m = 0;

for (i = 0; i < sizeof(val) * CHAR_BIT; i++) {
/* 10 is left margin of spaces in the above string */
/* I have to smoothly move along the buffer to print 1 or 0
according to the port's number */

m += snprintf(buf + 10 + m, sizeof(buf), (val & 0x00000001) ? "1 " :
"0 "); /* XXX */
val = val << 1;
}
fprintf(stdout, "%s\n", buf);

This code is very clumsy and, in my understanding, XXX invokes underfined
behavior, because 'm' is used in two sequence points?
What would you recommend to improve/change to make it look clean and solid?

Dealing only with your concern for undefined behavior: There is no
restriction on the number of times an object is used between sequence
points. The restriction is on the number of times it is modified
between sequence points (max of 1).

Furthermore, there is a sequence point after the arguments to snprintf
are evaluated but before the function is called. There is also a
sequence point when the function returns.

If we consider
m += snprintf(...);
to be the same as
m = m + ... snprintf(...);
then it is clear that m is modified only once. So the question of
undefined behavior hinges on whether the value of m being evaluated is
being used to determine the new value.

If snprintf is called first and then m is evaluated, the sequence is
evaluated arguments (including m)
sequence point
call snprintf
sequence point
evaluate m
update m
sequence point for semicolon
Between the sequence points where m is updated, it is updated only
once and the evaluation is only to determine the new value.

If m is evaluated first and then snprintf is called, the sequence is
evaluate m
evaluate arguments (including evaluating m again)
sequence point
call snprintf
sequence point
update m
sequence point for semicolon
Obviously, between the sequence points where m is updated, it is
updated once and never evaluated.

It is possible for m and the arguments to be evaluated first, then
call snprintf, and then update m. In this case, once snprintf is
called, the sequence is the same as above.

Therefore, undefined behavior of the type you describe does not occur.

I'm also pretty sure
x = x + x*x - 1/x;
does not violate the intent of 6.5-2.
 
D

David Thompson

Mark said:
puts(" 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
20 21 22 23 24");
uint32_t val = 0x1234567; /* for this example */
char buf[66] = " "; /* 6 spaces */
int i, m = 0;

for (i = 0; i < sizeof(val) * CHAR_BIT; i++) {
/* 10 is left margin of spaces in the above string */
/* I have to smoothly move along the buffer to print 1 or 0
according to the port's number */

m += snprintf(buf + 10 + m, sizeof(buf), (val & 0x00000001) ?
"1 " : "0 "); /* XXX */
val = val << 1;
}
(1) 66 is not enough space if the 0/1s are to line up under the numbers.
(2) You start with 6 spaces and then skip 10 places.

Yes, plus the label line shown is actually indented *14*.
(3) val = val << 1; is not right. You probably mean val = val >> 1;
(4) The snprintf call is wrong: the space available is not sizeof(buf)
because you pass an ever increasing pointer to it.

Yes, plus you don't need snprintf at all for the logic shown, if you
make buf big enough as you need to anyway.
(5) You seem to be putting the 0/1s into consecutive places (i.e. with
no space between them).

(6) The 0/1 won't line up under printed number unless you move on two
spaces for bit numbers > 9.

Yes. Or use all two-char labels e.g. 01 02 03 ... 09 10 ....
(7) Even if you fix number 2, you never null termiate the string that
you print in the last line.

No; snprintf does null-terminate unless n is zero. Even if it
truncates. In this loop all but the last are then overwritten.
(8) I'd write 1 rather than 0x00000001. They mean the same thing.
(9) You loop (and therefore write) at least 32 bits rather than 24.
Yes.

(10) Why are you using a buffer rather than just printing the bits?

The biggest suggestion I have is not to use a buffer but that seems to
be built into your question for some reason. Why?
Mu.

If I had to use a buffer, I'd:
(a) make it big enough (indent + 23 + 24 + 24 - 9 + 1) bytes;
(b) fill it with spaces and null terminate it;
(c) put the 0/1s into place using something like

buf[indent + i + (i > 9)] = val & 1 ? '1' : '0';

More like buf [ indent + i*2 + (i>9)*(i-9) -1 ] .

But I consider this yucky and fragile; I've been bit too many times by
code that computes a place in the middle of some big and complicated
thing, and especially after maintenance elsewhere it *sometimes*
doesn't get quite the right place. Where it's cheap and simple enough
to build in sequence, which it is here, I would just allocate 24*3 +
slop and use the moving index (or pointer) like:
m = indent;
...
m += sprintf (buf+m, "%*c", i>9?3:2, (val&1)+'0');

or to be cleverer, probably unduly so:
static const char threecharvals [2] [4] = { " 0", " 1" };
...
m += strlen (strcpy (buf+m, threecharvals[val&1] + (i<=9) ) );

and maybe assert (m < sizeof buf) to catch maintenance screwup.

With the obvious adjustments if I went to all-3-wide per above.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top