print bits of unsigned value

Discussion in 'C Programming' started by Mark, Jun 15, 2010.

  1. Mark

    Mark Guest

    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?

    --
    Mark
     
    Mark, Jun 15, 2010
    #1
    1. Advertising

  2. Mark

    bart.c Guest

    "Mark" <> wrote in message
    news:hv7mvs$rcs$...
    >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 */

    --
    Bartc
     
    bart.c, Jun 15, 2010
    #2
    1. Advertising

  3. "Mark" <> writes:

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

    --
    Ben.
     
    Ben Bacarisse, Jun 15, 2010
    #3
  4. Mark

    ImpalerCore Guest

    On Jun 15, 7:08 am, "Mark" <> wrote:
    > 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.
     
    ImpalerCore, Jun 15, 2010
    #4
  5. Mark

    bart.c Guest

    "bart.c" <> wrote in message
    news:pCJRn.53188$nW1.44746@hurricane...
    >
    > "Mark" <> wrote in message
    > news:hv7mvs$rcs$...
    >>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:

    ....

    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");

    }

    --
    Bartc
     
    bart.c, Jun 15, 2010
    #5
  6. Mark

    Mark Guest

    "Ben Bacarisse" <> wrote in message
    news:...
    [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.

    --
    Mark
     
    Mark, Jun 16, 2010
    #6
  7. On Tue, 15 Jun 2010 20:08:44 +0900, "Mark"
    <> wrote:

    >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.

    --
    Remove del for email
     
    Barry Schwarz, Jun 16, 2010
    #7
  8. On Tue, 15 Jun 2010 12:43:36 +0100, Ben Bacarisse
    <> wrote:

    > "Mark" <> writes:
    >
    > > 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).


    No; look closely: he had "0<sp>" and "1<sp>". It's only one space not
    two where needed (as you say next), but it is one.

    > (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.
     
    David Thompson, Jul 2, 2010
    #8
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. sarmin kho
    Replies:
    2
    Views:
    828
    A. Lloyd Flanagan
    Jun 15, 2004
  2. Miki Tebeka
    Replies:
    1
    Views:
    446
    Marcin 'Qrczak' Kowalczyk
    Jun 14, 2004
  3. Tomás

    Value Bits Vs Object Bits

    Tomás, Jun 2, 2006, in forum: C Programming
    Replies:
    13
    Views:
    549
    Hallvard B Furuseth
    Jul 1, 2006
  4. Replies:
    3
    Views:
    421
    James Kanze
    Nov 19, 2008
  5. pozz
    Replies:
    12
    Views:
    745
    Tim Rentsch
    Mar 20, 2011
Loading...

Share This Page