embedded software boot camp

Binary Literals in C

Wednesday, September 30th, 2009 by Michael Barr

A couple of years ago, Netrino engineer Dan Smith was writing stepper motor control firmware that interfaced to lots of registers with binary fields and sub-fields. After struggling a bit with the usual error-prone “off by 1 bit shift” masking and conversion from binary to hexadecimal literals in C, he happened across a useful post on a forum.

In a nutshell, the “binary literal” technique involves the following set of C preprocessor macros:

// Internal Macros
#define HEX__(n) 0x##n##LU
#define B8__(x) ((x&0x0000000FLU)?1:0) \
+((x&0x000000F0LU)?2:0) \
+((x&0x00000F00LU)?4:0) \
+((x&0x0000F000LU)?8:0) \
+((x&0x000F0000LU)?16:0) \
+((x&0x00F00000LU)?32:0) \
+((x&0x0F000000LU)?64:0) \

// User-visible Macros
#define B8(d) ((unsigned char)B8__(HEX__(d)))
#define B16(dmsb,dlsb) (((unsigned short)B8(dmsb)<<8) + B8(dlsb))
#define B32(dmsb,db2,db3,dlsb) \
(((unsigned long)B8(dmsb)<<24) \
+ ((unsigned long)B8(db2)<<16) \
+ ((unsigned long)B8(db3)<<8) \
+ B8(dlsb))

Here are some examples of the usage of these macros:
B8(01010101) // 85
B16(10101010,01010101) // 43,605
B32(10000000,11111111,10101010,01010101) // 2,164,238,933

So if you had a memory-mapped 8-bit control register of the format XXXYYZZZ (where XXX, YY, and ZZZ are subfields), you could initialize it like so:

*p_reg = ( (B8(010) << 5) | (B8(11) << 3) | (B8(101) << 0) )

which sets the XXX bits to 010, YY to 11, and ZZZ to 101. If I ever needed to change XXX to 011, just change a single 0 to a 1 in the source code, and everything magically changes. Best of all, it’s all done at compile-time. No error-prone conversion to hexadecimal necessary, no figuring out which bits belong to which nibbles, etc.

What is that old saying? — “good programmers write good code; great programmers steal great code

Tags: ,

13 Responses to “Binary Literals in C”

  1. Nigel Jones says:

    Great post (or perhaps steal :-)). I've always wanted a way to express binary numbers in C, and now I have one! I took the code and experimented a bit with it. If you turn optimization completely off, them my IAR ARM compiler did include some redundant code. However, the lowest level of optimization fixed that problem.The MISRA compliance checker did of course have an absolute fit over the use of these macros, generating a large number of complaints. I think this is yet another example where one has to make an intelligent trade off. Should I use a macro that is unsafe by MISRA standards, yet allows me to eliminate an entire class of errors?Perhaps in my copious free time I'll see if I can restructure the macros to make MISRA compliance possible.

  2. Nick says:

    MISRA compliance allows exceptions for exactly this kind of thingThis is making your code safer and not less safe:The macros encapsulate individually unsafe constructs but package them up with a very safe interface.You just need a project-wide MISRA deviation specialised to these three macros and you can retain full MISRA compliance, both in the spirit and the letter of the law.Different MISRA checkers handle deviations in different ways but PC-Lint handles macro-specific deviations very nicely, for one example.

  3. Kenneth says:

    I'm trying to learn the finer points of C and how to be creative with it. :)I don't understand something the binary macros. —————————-/* 8-bit conversion function */#define B8__(x) ((x&0x0000000FLU)?1:0) \+((x&0x000000F0LU)?2:0) \+((x&0x00000F00LU)?4:0) \+((x&0x0000F000LU)?8:0) \+((x&0x000F0000LU)?16:0) \+((x&0x00F00000LU)?32:0) \+((x&0x0F000000LU)?64:0) \+((x&0xF0000000LU)?128:0)——————————-If we take part of the first line:(x&0x0000000FLU)It would appear to me that x is being anded with the constant 0x0000000FLU.Its with this constant that I get lost. To me 0x0000000F = 15 (in decimal). The "LU" as I underatand it, and I could be wrong, signifies the constant is an Unsigned Long.I understand how the macro is supposed to work. My problem, I guess is with the syntax.Can someone shed some light?Thanks,Kennykenl@anspach.com

  4. Nigel Jones says:

    Sure can. By way of example, lets look at B8(1011).First off the HEX__ macro converts the argument 1011 into 0x1011UL. It does this via the stringize operator ##. (This is a pretty obscure part of the preprocessor – you'll find more information online).Now let's see what happens with the B8__(x) macro. It consists of 8 lines, each of which effectively tests one bit position. The first line is (x & 0x0000000Flu) ? 1 : 0. (Note that I've added spaces and made the LU lower case, thus making things a little clearer IMHO).Anyway, this becomes: 0x1011ul & 0x0000000Ful = 0x1. This evidently evaluates to true and so the ternary operator returns 1.The second line of the B8__() macro is (x & 0x000000F0lu) ? 2 : 0). Thus this expands to 0x1011 & 0x000000F0 = 0x10. This evidently evaluates to true and so the ternary operator returns 2, which is added to the previous result, giving a total of decimal 3.The third line of the B8__() macro is (x & 0x00000F00lu) ? 4 : 0). Thus this expands to 0x1011 & 0x00000F0 = 0x0. This evidently evaluates to false and so the ternary operator returns 0, which is added to the previous result, keeping our total at decimal 3.The fourth line of the B8__() macro is (x & 0x0000F000lu) ? 8 : 0). Thus this expands to 0x1011 & 0x0000F000 = 0x1000. This evidently evaluates to true and so the ternary operator returns 8, which is added to the previous result, giving a total of decimal 11 – and our final result.The B16 and B32 macros simply build upon this technique by using appropriate casts and shifts.Hope this helps. If not post again and I'll try and expand upon the explanation.

  5. Miro Samek says:

    Indeed, MISRA checker does not particularly like the macros, because they violate the following MISRA rules:macro HEX__(n) Violates MISRA Required Rule 98, Multiple use of '#/##' operators in macro definitionmacro B8__ Violates MISRA Rule 7, Trigraphs shall not be usedmacro B8__ Violates MISRA Rule 96, Expression-like macro '' not parenthesizedBut the macros can be restructured as follows to comply with MISRA:#define HEX__(n_) ((uint32_t)0x##n_)#define B8__(x_) \ ( ((x_) & 0x1LU) \ | (((x_) & 0x10LU) >> 3) \ | (((x_) & 0x100LU) >> 6) \ | (((x_) & 0x1000LU) >> 9) \ | (((x_) & 0x10000LU) >> 12) \ | (((x_) & 0x100000LU) >> 15) \ | (((x_) & 0x1000000LU) >> 18) \ | (((x_) & 0x10000000LU) >> 21))#define B8(b0_) ((uint8_t)B8__(HEX__(b0_)))#define B16(b1_,b0_) \ ((uint16_t)(B8__(HEX__(b0_)) | (B8__(HEX__(b1_)) << 8)))#define B32(b3_,b2_,b1_,b0_) \ ((uint32_t)(B8__(HEX__(b0_)) \ | (B8__(HEX__(b1_)) << 8) \ | (B8__(HEX__(b2_)) << 16) \ | (B8__(HEX__(b3_)) << 24)))While the macros are MISRA compliant, the actual use can sometimes lead to violating MISRA rule 19 (octal constant used), if you start your binary literal with a zero. Miro Samek

    • Lundin says:

      There are no trigraphs in this code. It would seem that your MISRA checker is a bit too trigger-happy (they all are). Upon manual code inspection, I can spot the following MISRA violations: use of function-like macros (19.7), only one occurance of ## in one macro (19.12), ## should not be used (19.13), U suffix required on integer literals (10.6), no implicit type conversions (10.1), octal integer literals should not be used (7.1), uintn_t should be used instead of the default integer data types (6.3), C99 should not be used (1.1).

  6. […] topics, if you have not read Mike Barr’s recent posting on binary literals, then I strongly recommend that you do so. It would have fitted very nicely into […]

  7. Lundin says:

    I never quite understood the need of binary literals. Back in school they wouldn’t let us write our first “hello world” before we knew binary and hex. I think you should be able to safely assume that every programmer can read hex. Apart from that, I personally think that *p_reg = ( (B8(010) << 5) | (B8(11) << 3) | (B8(101) << 0) ) is far harder to read than *p_reg = (2<<5) | (3<<5) | (5<<0);, not to mention *p_reg = X| Y | Z; where X Y Z are bit mask constants. In a real application, those bit masks would of course have meaningful names instead.

    • Peter P says:

      B32( 11111100 ,
      11000000 ,
      11000000 ,
      11111100 ),
      B32( 11000000 ,
      11000000 ,
      11000000 ,
      00000000 )

      obviously is an ‘F’ character image. Which you wouldn’t see if it were written “0xFCC0C0FC, 0xC0C0C000”.

      • Ricky says:

        If you want to do character images, you might want to use these macros:


        #define G8(n0) ((uint8_t) (n0))
        //!< Build a byte image

        #define G16(n1, n0) ((uint16_t) (((n1) << 8) | (n0)))
        //!< Build a halfword image

        #define G32(n3, n2, n1, n0)
        ((uint32_t) ((G16 ((n3), (n2)) << 16) | G16 ((n1), (n0))))
        //!< Build a word image

        #define G64(n7, n6, n5, n4, n3, n2, n1, n0)
        ((uint64_t) ((G32 ((n7), (n6), (n5), (n4)) * 0x100000000lu)
        | G32 ((n3), (n2), (n1), (n0))))
        //!< Build a long image

        Now you can do an F like so:

        G8 (OOOOOO__);
        G8 (OO______);
        G8 (OO______);
        G8 (OOOOOO__);
        G8 (OO______);
        G8 (OO______);
        G8 (OO______);
        G8 (________);

        By the way, upper case Os are used in the macros, not zeros.

    • Peter Painter says:

      It of course doesn’t make sense in the examples you give. However, sometimes a bit pattern is exactly that: a pattern. Imagine font data. If written as binary literals, you can ‘see’ the font letters (and any mistakes) while hex values are meaningless letters.
      Whenever a value represents some visual pattern, binary literals are an invaluable help.
      Actually, that’s my main use for binary literals.

  8. Julian Day says:

    Hmm, I’m not sure quite whether I like these or not. When working in C/C++, I’m very used to not having binary numeric literals and to suddenly see this slightly throws me. When working in Ada and other languages with binary numeric literals, I can hardly remember either using them or seeing them used.

    I’d be interested in seeing a strong case for using them, since I agree with Lundin above re the *p_reg example, but would actually use a macro or const for each of the fields rather than numeric literals.

    In any case, from a MISRA 2004 perspective, I think the biggest obstacle is rule 13.7, which states that “Boolean operations whose results are invariant shall not be permitted.” That’s the whole point of these macros, to take advantage of this invariance to generate a numeric literal from this binary representation at compile time. You can put disable warnings about the macro definitions and make the expansions MISRA type compliant, but you would need to ensure that 13.7 is not in effect at the point where the macro is expanded, since the expansion itself is not MISRAble, if that is the correct adjective.

    For reference, here’s a version of the macro that could be used with the GHS compiler’s built in rules checking:

    #ifndef BIN_H_
    #define BIN_H_


    /* Internal Macros */

    #pragma ghs startnomisra

    #define HEX__(n) 0x##n##LU
    #define B8__(x) ((uint8_t)(((x&0x0000000FLU)!=0UL)?1UL:0UL) \
    +(((x&0x000000F0LU)!=0UL)?2UL:0UL) \
    +(((x&0x00000F00LU)!=0UL)?4UL:0UL) \
    +(((x&0x0000F000LU)!=0UL)?8UL:0UL) \
    +(((x&0x000F0000LU)!=0UL)?16UL:0UL) \
    +(((x&0x00F00000LU)!=0UL)?32UL:0UL) \
    +(((x&0x0F000000LU)!=0UL)?64UL:0UL) \

    /* User-visible Macros */
    #define B8(d) ((uint8_t)B8__(HEX__(d)))
    #define B16(dmsb,dlsb) ((uint16_t)((uint16_t)(B8(dmsb)<<8) + ((uint16_t)B8(dlsb))))
    #define B32(dmsb,db2,db3,dlsb) \
    ((uint32_t)((uint32_t)B16(dmsb,db2)<<16) + ((uint32_t)B16(db3,dlsb)))

    #pragma ghs endnomisra

    #endif /* BIN_H_ */

    The GHS compiler would evaluate the binary expresssions as the equivalent numeric literal at any optimisation level, which after all is the point of them.

    • Julian Day says:

      I’ve just noticed that the #include in the above is missing the stdint.h part of the line, probably because of the angle brackets.

Leave a Reply